From bc33816d8d3ef280c30a0d53417640b130a8c06e Mon Sep 17 00:00:00 2001 From: Roma Date: Mon, 9 Jun 2025 13:52:46 +0300 Subject: [PATCH 01/34] refactor(preprint-hero): Extracted logic of handling search input --- .../preprint-provider-hero.component.html | 3 +-- .../preprint-provider-hero.component.ts | 17 +++++------------ .../preprint-provider-overview.component.html | 1 + .../preprint-provider-overview.component.ts | 10 +++++++++- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/app/features/preprints/components/preprint-provider-hero/preprint-provider-hero.component.html b/src/app/features/preprints/components/preprint-provider-hero/preprint-provider-hero.component.html index a80aad27c..392efc63b 100644 --- a/src/app/features/preprints/components/preprint-provider-hero/preprint-provider-hero.component.html +++ b/src/app/features/preprints/components/preprint-provider-hero/preprint-provider-hero.component.html @@ -56,9 +56,8 @@

{{ preprintProvider()!.name }}

diff --git a/src/app/features/preprints/components/preprint-provider-hero/preprint-provider-hero.component.ts b/src/app/features/preprints/components/preprint-provider-hero/preprint-provider-hero.component.ts index d59d62340..c8d671027 100644 --- a/src/app/features/preprints/components/preprint-provider-hero/preprint-provider-hero.component.ts +++ b/src/app/features/preprints/components/preprint-provider-hero/preprint-provider-hero.component.ts @@ -6,13 +6,11 @@ import { Skeleton } from 'primeng/skeleton'; import { NgOptimizedImage } from '@angular/common'; import { ChangeDetectionStrategy, Component, inject, input, output } from '@angular/core'; -import { FormControl } from '@angular/forms'; -import { Router, RouterLink } from '@angular/router'; +import { RouterLink } from '@angular/router'; import { PreprintsHelpDialogComponent } from '@osf/features/preprints/components'; import { PreprintProviderDetails } from '@osf/features/preprints/models'; import { SearchInputComponent } from '@shared/components'; -import { ResourceTab } from '@shared/enums'; import { DecodeHtmlPipe } from '@shared/pipes'; @Component({ @@ -26,23 +24,18 @@ import { DecodeHtmlPipe } from '@shared/pipes'; export class PreprintProviderHeroComponent { protected translateService = inject(TranslateService); protected dialogService = inject(DialogService); - private readonly router = inject(Router); + preprintProvider = input.required(); isPreprintProviderLoading = input.required(); addPreprintClicked = output(); - - protected searchControl = new FormControl(''); + triggerSearch = output(); addPreprint() { this.addPreprintClicked.emit(); } - redirectToDiscoverPageWithValue() { - const searchValue = this.searchControl.value; - - this.router.navigate(['/discover'], { - queryParams: { search: searchValue, resourceTab: ResourceTab.Preprints }, - }); + onTriggerSearch(value: string) { + this.triggerSearch.emit(value); } openHelpDialog() { diff --git a/src/app/features/preprints/pages/preprint-provider-overview/preprint-provider-overview.component.html b/src/app/features/preprints/pages/preprint-provider-overview/preprint-provider-overview.component.html index c0f7884d4..e4757725c 100644 --- a/src/app/features/preprints/pages/preprint-provider-overview/preprint-provider-overview.component.html +++ b/src/app/features/preprints/pages/preprint-provider-overview/preprint-provider-overview.component.html @@ -1,6 +1,7 @@ params['providerId'])) ?? of(undefined)); private actions = createDispatchMap({ @@ -67,4 +68,11 @@ export class PreprintProviderOverviewComponent implements OnInit, OnDestroy { HeaderStyleHelper.resetToDefaults(); BrandService.resetBranding(); } + + redirectToDiscoverPageWithValue(searchValue: string) { + this.router.navigate(['discover'], { + relativeTo: this.route, + queryParams: { search: searchValue }, + }); + } } From 3a2fc45fd6725aa9f0bd48e6715309522feb51e8 Mon Sep 17 00:00:00 2001 From: Roma Date: Tue, 10 Jun 2025 14:27:24 +0300 Subject: [PATCH 02/34] feat(preprints-discover): Added discover page, registered route, basic template --- .../preprints/constants/preprints.routes.ts | 7 +++ src/app/features/preprints/pages/index.ts | 1 + .../preprint-provider-discover.component.html | 4 ++ .../preprint-provider-discover.component.scss | 0 ...eprint-provider-discover.component.spec.ts | 22 ++++++++ .../preprint-provider-discover.component.ts | 54 +++++++++++++++++++ 6 files changed, 88 insertions(+) create mode 100644 src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.html create mode 100644 src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.scss create mode 100644 src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.spec.ts create mode 100644 src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.ts diff --git a/src/app/features/preprints/constants/preprints.routes.ts b/src/app/features/preprints/constants/preprints.routes.ts index e5a7bd24e..c21fdbd4b 100644 --- a/src/app/features/preprints/constants/preprints.routes.ts +++ b/src/app/features/preprints/constants/preprints.routes.ts @@ -26,6 +26,13 @@ export const preprintsRoutes: Routes = [ (c) => c.PreprintProviderOverviewComponent ), }, + { + path: 'overview/:providerId/discover', + loadComponent: () => + import('@osf/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component').then( + (c) => c.PreprintProviderDiscoverComponent + ), + }, ], }, ]; diff --git a/src/app/features/preprints/pages/index.ts b/src/app/features/preprints/pages/index.ts index b96b4bc7a..e79f5f462 100644 --- a/src/app/features/preprints/pages/index.ts +++ b/src/app/features/preprints/pages/index.ts @@ -1,2 +1,3 @@ export { PreprintsLandingComponent } from '@osf/features/preprints/pages/landing/preprints-landing.component'; +export { PreprintProviderDiscoverComponent } from '@osf/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component'; export { PreprintProviderOverviewComponent } from '@osf/features/preprints/pages/preprint-provider-overview/preprint-provider-overview.component'; diff --git a/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.html b/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.html new file mode 100644 index 000000000..bfa76be2a --- /dev/null +++ b/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.html @@ -0,0 +1,4 @@ + diff --git a/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.scss b/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.spec.ts b/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.spec.ts new file mode 100644 index 000000000..7b018d31b --- /dev/null +++ b/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PreprintProviderDiscoverComponent } from './preprint-provider-discover.component'; + +describe('PreprintProviderDiscoverComponent', () => { + let component: PreprintProviderDiscoverComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PreprintProviderDiscoverComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(PreprintProviderDiscoverComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.ts b/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.ts new file mode 100644 index 000000000..9d3b0a692 --- /dev/null +++ b/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.ts @@ -0,0 +1,54 @@ +import { createDispatchMap, select } from '@ngxs/store'; + +import { map, of } from 'rxjs'; + +import { ChangeDetectionStrategy, Component, effect, inject, OnDestroy, OnInit } from '@angular/core'; +import { toSignal } from '@angular/core/rxjs-interop'; +import { ActivatedRoute } from '@angular/router'; + +import { PreprintProviderHeroComponent } from '@osf/features/preprints/components/preprint-provider-hero/preprint-provider-hero.component'; +import { BrandService } from '@osf/features/preprints/services'; +import { GetPreprintProviderById, PreprintsSelectors } from '@osf/features/preprints/store'; +import { HeaderStyleHelper } from '@shared/utils'; + +@Component({ + selector: 'osf-preprint-provider-discover', + imports: [PreprintProviderHeroComponent], + templateUrl: './preprint-provider-discover.component.html', + styleUrl: './preprint-provider-discover.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class PreprintProviderDiscoverComponent implements OnInit, OnDestroy { + private readonly route = inject(ActivatedRoute); + private providerId = toSignal(this.route.params.pipe(map((params) => params['providerId'])) ?? of(undefined)); + + private actions = createDispatchMap({ + getPreprintProviderById: GetPreprintProviderById, + }); + preprintProvider = select(PreprintsSelectors.getPreprintProviderDetails(this.providerId())); + isPreprintProviderLoading = select(PreprintsSelectors.isPreprintProviderDetailsLoading); + + constructor() { + effect(() => { + const provider = this.preprintProvider(); + + if (provider) { + BrandService.applyBranding(provider.brand); + HeaderStyleHelper.applyHeaderStyles( + provider.brand.primaryColor, + provider.brand.secondaryColor, + provider.brand.heroBackgroundImageUrl + ); + } + }); + } + + ngOnInit() { + this.actions.getPreprintProviderById(this.providerId()); + } + + ngOnDestroy() { + HeaderStyleHelper.resetToDefaults(); + BrandService.resetBranding(); + } +} From 8cf3b64a3fe1a9c18c366b5407ef64bbbfbb3e9c Mon Sep 17 00:00:00 2001 From: Roma Date: Tue, 10 Jun 2025 14:31:46 +0300 Subject: [PATCH 03/34] refactor(resource-req-type): Moved enum to shared --- src/app/features/search/store/search.state.ts | 2 +- .../search => shared}/enums/get-resources-request-type.enum.ts | 0 src/app/shared/enums/index.ts | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) rename src/app/{features/search => shared}/enums/get-resources-request-type.enum.ts (100%) diff --git a/src/app/features/search/store/search.state.ts b/src/app/features/search/store/search.state.ts index af5695c58..48d9cac87 100644 --- a/src/app/features/search/store/search.state.ts +++ b/src/app/features/search/store/search.state.ts @@ -4,9 +4,9 @@ import { BehaviorSubject, EMPTY, switchMap, tap } from 'rxjs'; import { inject, Injectable } from '@angular/core'; -import { GetResourcesRequestTypeEnum } from '@osf/features/search/enums/get-resources-request-type.enum'; import { SearchService } from '@osf/shared/services'; import { addFiltersParams, getResourceTypes } from '@osf/shared/utils'; +import { GetResourcesRequestTypeEnum } from '@shared/enums'; import { ResourceFiltersSelectors } from '../components/resource-filters/store'; import { searchStateDefaults } from '../utils/data'; diff --git a/src/app/features/search/enums/get-resources-request-type.enum.ts b/src/app/shared/enums/get-resources-request-type.enum.ts similarity index 100% rename from src/app/features/search/enums/get-resources-request-type.enum.ts rename to src/app/shared/enums/get-resources-request-type.enum.ts diff --git a/src/app/shared/enums/index.ts b/src/app/shared/enums/index.ts index 5b89b32f6..db1b959a3 100644 --- a/src/app/shared/enums/index.ts +++ b/src/app/shared/enums/index.ts @@ -2,6 +2,7 @@ export * from './breakpoint-queries.enum'; export * from './create-component-form-controls.enum'; export * from './create-project-form-controls.enum'; export * from './filter-type.enum'; +export * from './get-resources-request-type.enum'; export * from './resource-tab.enum'; export * from './resource-type.enum'; export * from './share-indexing.enum'; From dfd16cef2293f9849d46fe9288aa1db7cc03d0e4 Mon Sep 17 00:00:00 2001 From: Roma Date: Tue, 10 Jun 2025 14:40:17 +0300 Subject: [PATCH 04/34] refactor(resource-filter-label): Moved model to shared --- .../store/my-profile-resource-filters.model.ts | 8 ++------ .../store/my-profile-resource-filters.selectors.ts | 2 +- .../resource-filters/store/resource-filters.model.ts | 8 ++------ .../resource-filters/store/resource-filters.selectors.ts | 4 +++- src/app/shared/models/filters/index.ts | 1 + src/app/shared/models/filters/resource-filter-label.ts | 5 +++++ 6 files changed, 14 insertions(+), 14 deletions(-) create mode 100644 src/app/shared/models/filters/resource-filter-label.ts diff --git a/src/app/features/my-profile/components/my-profile-resource-filters/store/my-profile-resource-filters.model.ts b/src/app/features/my-profile/components/my-profile-resource-filters/store/my-profile-resource-filters.model.ts index e1b20d42a..441399cea 100644 --- a/src/app/features/my-profile/components/my-profile-resource-filters/store/my-profile-resource-filters.model.ts +++ b/src/app/features/my-profile/components/my-profile-resource-filters/store/my-profile-resource-filters.model.ts @@ -1,3 +1,5 @@ +import { ResourceFilterLabel } from '@shared/models'; + export interface MyProfileResourceFiltersStateModel { creator: ResourceFilterLabel; dateCreated: ResourceFilterLabel; @@ -9,9 +11,3 @@ export interface MyProfileResourceFiltersStateModel { provider: ResourceFilterLabel; partOfCollection: ResourceFilterLabel; } - -export interface ResourceFilterLabel { - filterName: string; - label?: string; - value?: string; -} diff --git a/src/app/features/my-profile/components/my-profile-resource-filters/store/my-profile-resource-filters.selectors.ts b/src/app/features/my-profile/components/my-profile-resource-filters/store/my-profile-resource-filters.selectors.ts index 470e56136..4d7564ab6 100644 --- a/src/app/features/my-profile/components/my-profile-resource-filters/store/my-profile-resource-filters.selectors.ts +++ b/src/app/features/my-profile/components/my-profile-resource-filters/store/my-profile-resource-filters.selectors.ts @@ -1,8 +1,8 @@ import { Selector } from '@ngxs/store'; import { ResourceFiltersStateModel } from '@osf/features/search/components/resource-filters/store'; +import { ResourceFilterLabel } from '@shared/models'; -import { ResourceFilterLabel } from './my-profile-resource-filters.model'; import { MyProfileResourceFiltersState } from './my-profile-resource-filters.state'; export class MyProfileResourceFiltersSelectors { diff --git a/src/app/features/search/components/resource-filters/store/resource-filters.model.ts b/src/app/features/search/components/resource-filters/store/resource-filters.model.ts index a8d2ce815..c58b9fba6 100644 --- a/src/app/features/search/components/resource-filters/store/resource-filters.model.ts +++ b/src/app/features/search/components/resource-filters/store/resource-filters.model.ts @@ -1,3 +1,5 @@ +import { ResourceFilterLabel } from '@osf/shared/models'; + export interface ResourceFiltersStateModel { creator: ResourceFilterLabel; dateCreated: ResourceFilterLabel; @@ -9,9 +11,3 @@ export interface ResourceFiltersStateModel { provider: ResourceFilterLabel; partOfCollection: ResourceFilterLabel; } - -export interface ResourceFilterLabel { - filterName: string; - label?: string; - value?: string; -} diff --git a/src/app/features/search/components/resource-filters/store/resource-filters.selectors.ts b/src/app/features/search/components/resource-filters/store/resource-filters.selectors.ts index eaed18ed3..2055b759d 100644 --- a/src/app/features/search/components/resource-filters/store/resource-filters.selectors.ts +++ b/src/app/features/search/components/resource-filters/store/resource-filters.selectors.ts @@ -1,6 +1,8 @@ import { Selector } from '@ngxs/store'; -import { ResourceFilterLabel, ResourceFiltersStateModel } from './resource-filters.model'; +import { ResourceFilterLabel } from '@shared/models'; + +import { ResourceFiltersStateModel } from './resource-filters.model'; import { ResourceFiltersState } from './resource-filters.state'; export class ResourceFiltersSelectors { diff --git a/src/app/shared/models/filters/index.ts b/src/app/shared/models/filters/index.ts index fe8e5ad79..375df8e0a 100644 --- a/src/app/shared/models/filters/index.ts +++ b/src/app/shared/models/filters/index.ts @@ -7,6 +7,7 @@ export * from './institution'; export * from './license'; export * from './part-of-collection'; export * from './provider'; +export * from './resource-filter-label'; export * from './resource-type'; export * from './search-filters.model'; export * from './search-result-count.model'; diff --git a/src/app/shared/models/filters/resource-filter-label.ts b/src/app/shared/models/filters/resource-filter-label.ts new file mode 100644 index 000000000..8d7d6693a --- /dev/null +++ b/src/app/shared/models/filters/resource-filter-label.ts @@ -0,0 +1,5 @@ +export interface ResourceFilterLabel { + filterName: string; + label?: string; + value?: string; +} From afa45f3e00a1f45518c6ffed2a4c63332066c044 Mon Sep 17 00:00:00 2001 From: Roma Date: Tue, 10 Jun 2025 16:04:08 +0300 Subject: [PATCH 05/34] refactor(search-state-defaults): Moved constant to shared --- src/app/features/my-profile/store/my-profile.state.ts | 2 +- src/app/features/search/store/search.state.ts | 2 +- src/app/features/search/utils/index.ts | 1 - src/app/shared/constants/index.ts | 2 +- .../data.ts => shared/constants/search-state-defaults.const.ts} | 2 +- 5 files changed, 4 insertions(+), 5 deletions(-) delete mode 100644 src/app/features/search/utils/index.ts rename src/app/{features/search/utils/data.ts => shared/constants/search-state-defaults.const.ts} (80%) diff --git a/src/app/features/my-profile/store/my-profile.state.ts b/src/app/features/my-profile/store/my-profile.state.ts index 50df360e0..3aa8cb977 100644 --- a/src/app/features/my-profile/store/my-profile.state.ts +++ b/src/app/features/my-profile/store/my-profile.state.ts @@ -15,9 +15,9 @@ import { SetSearchText, SetSortBy, } from '@osf/features/my-profile/store'; -import { searchStateDefaults } from '@osf/features/search/utils/data'; import { SearchService } from '@osf/shared/services'; import { addFiltersParams, getResourceTypes } from '@osf/shared/utils'; +import { searchStateDefaults } from '@shared/constants'; import { MyProfileResourceFiltersSelectors } from '../components/my-profile-resource-filters/store'; diff --git a/src/app/features/search/store/search.state.ts b/src/app/features/search/store/search.state.ts index 48d9cac87..5fa319f19 100644 --- a/src/app/features/search/store/search.state.ts +++ b/src/app/features/search/store/search.state.ts @@ -6,10 +6,10 @@ import { inject, Injectable } from '@angular/core'; import { SearchService } from '@osf/shared/services'; import { addFiltersParams, getResourceTypes } from '@osf/shared/utils'; +import { searchStateDefaults } from '@shared/constants'; import { GetResourcesRequestTypeEnum } from '@shared/enums'; import { ResourceFiltersSelectors } from '../components/resource-filters/store'; -import { searchStateDefaults } from '../utils/data'; import { GetResources, diff --git a/src/app/features/search/utils/index.ts b/src/app/features/search/utils/index.ts deleted file mode 100644 index 370767922..000000000 --- a/src/app/features/search/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './data'; diff --git a/src/app/shared/constants/index.ts b/src/app/shared/constants/index.ts index 9c28141dd..d5fcb54f2 100644 --- a/src/app/shared/constants/index.ts +++ b/src/app/shared/constants/index.ts @@ -1,4 +1,4 @@ export * from './input-limits.const'; export * from './input-validation-messages.const'; export * from './remove-nullable.const'; -export * from './scientists.const'; +export * from './search-state-defaults.const'; diff --git a/src/app/features/search/utils/data.ts b/src/app/shared/constants/search-state-defaults.const.ts similarity index 80% rename from src/app/features/search/utils/data.ts rename to src/app/shared/constants/search-state-defaults.const.ts index 0e89de1a1..19b9ddbc7 100644 --- a/src/app/features/search/utils/data.ts +++ b/src/app/shared/constants/search-state-defaults.const.ts @@ -1,4 +1,4 @@ -import { ResourceTab } from '@osf/shared/enums/resource-tab.enum'; +import { ResourceTab } from '@shared/enums'; export const searchStateDefaults = { resources: { From 85685bf7fe4efaed4fa9dbe8b895a71f1e8634df Mon Sep 17 00:00:00 2001 From: Roma Date: Tue, 10 Jun 2025 16:09:05 +0300 Subject: [PATCH 06/34] refactor(resource-filters-defaults): Moved constant to shared --- .../store/my-profile-resource-filters.state.ts | 6 ++++-- .../resource-filters/store/resource-filters.state.ts | 7 ++++--- src/app/shared/constants/index.ts | 1 + .../resource-filters-defaults.ts} | 4 ++-- src/app/shared/models/index.ts | 1 - 5 files changed, 11 insertions(+), 8 deletions(-) rename src/app/shared/{models/resource-filters-defaults.model.ts => constants/resource-filters-defaults.ts} (90%) diff --git a/src/app/features/my-profile/components/my-profile-resource-filters/store/my-profile-resource-filters.state.ts b/src/app/features/my-profile/components/my-profile-resource-filters/store/my-profile-resource-filters.state.ts index e5766bade..6bed76c34 100644 --- a/src/app/features/my-profile/components/my-profile-resource-filters/store/my-profile-resource-filters.state.ts +++ b/src/app/features/my-profile/components/my-profile-resource-filters/store/my-profile-resource-filters.state.ts @@ -3,7 +3,8 @@ import { Action, NgxsOnInit, State, StateContext, Store } from '@ngxs/store'; import { inject, Injectable } from '@angular/core'; import { UserSelectors } from '@osf/core/store/user'; -import { FilterLabelsModel, resourceFiltersDefaultsModel } from '@osf/shared/models'; +import { FilterLabelsModel } from '@osf/shared/models'; +import { resourceFiltersDefaults } from '@shared/constants'; import { SetCreator, @@ -21,7 +22,7 @@ import { MyProfileResourceFiltersStateModel } from './my-profile-resource-filter // Store for user selected filters values @State({ name: 'myProfileResourceFilters', - defaults: resourceFiltersDefaultsModel, + defaults: resourceFiltersDefaults, }) @Injectable() export class MyProfileResourceFiltersState implements NgxsOnInit { @@ -41,6 +42,7 @@ export class MyProfileResourceFiltersState implements NgxsOnInit { } }); } + @Action(SetCreator) setCreator(ctx: StateContext, action: SetCreator) { ctx.patchState({ diff --git a/src/app/features/search/components/resource-filters/store/resource-filters.state.ts b/src/app/features/search/components/resource-filters/store/resource-filters.state.ts index 5ab0ceb7f..8d541b3bf 100644 --- a/src/app/features/search/components/resource-filters/store/resource-filters.state.ts +++ b/src/app/features/search/components/resource-filters/store/resource-filters.state.ts @@ -2,7 +2,8 @@ import { Action, State, StateContext } from '@ngxs/store'; import { Injectable } from '@angular/core'; -import { FilterLabelsModel, resourceFiltersDefaultsModel } from '@osf/shared/models'; +import { FilterLabelsModel } from '@osf/shared/models'; +import { resourceFiltersDefaults } from '@shared/constants'; import { ResetFiltersState, @@ -21,7 +22,7 @@ import { ResourceFiltersStateModel } from './resource-filters.model'; // Store for user selected filters values @State({ name: 'resourceFilters', - defaults: resourceFiltersDefaultsModel, + defaults: resourceFiltersDefaults, }) @Injectable() export class ResourceFiltersState { @@ -126,6 +127,6 @@ export class ResourceFiltersState { @Action(ResetFiltersState) resetState(ctx: StateContext) { - ctx.patchState(resourceFiltersDefaultsModel); + ctx.patchState(resourceFiltersDefaults); } } diff --git a/src/app/shared/constants/index.ts b/src/app/shared/constants/index.ts index d5fcb54f2..aed11d166 100644 --- a/src/app/shared/constants/index.ts +++ b/src/app/shared/constants/index.ts @@ -1,4 +1,5 @@ export * from './input-limits.const'; export * from './input-validation-messages.const'; export * from './remove-nullable.const'; +export * from './resource-filters-defaults'; export * from './search-state-defaults.const'; diff --git a/src/app/shared/models/resource-filters-defaults.model.ts b/src/app/shared/constants/resource-filters-defaults.ts similarity index 90% rename from src/app/shared/models/resource-filters-defaults.model.ts rename to src/app/shared/constants/resource-filters-defaults.ts index 1522f3b31..c01ac7b5b 100644 --- a/src/app/shared/models/resource-filters-defaults.model.ts +++ b/src/app/shared/constants/resource-filters-defaults.ts @@ -1,6 +1,6 @@ -import { FilterLabelsModel } from './filter-labels.model'; +import { FilterLabelsModel } from '@shared/models'; -export const resourceFiltersDefaultsModel = { +export const resourceFiltersDefaults = { creator: { filterName: FilterLabelsModel.creator, label: undefined, diff --git a/src/app/shared/models/index.ts b/src/app/shared/models/index.ts index 6c9819889..74def860f 100644 --- a/src/app/shared/models/index.ts +++ b/src/app/shared/models/index.ts @@ -10,7 +10,6 @@ export * from './node-response.model'; export * from './paginated-data.model'; export * from './query-params.model'; export * from './resource-card'; -export * from './resource-filters-defaults.model'; export * from './select-option.model'; export * from './social-icon.model'; export * from './store'; From 70cdccd122d64c2cc9d76f0d0e147b1a693607ed Mon Sep 17 00:00:00 2001 From: Roma Date: Tue, 10 Jun 2025 16:10:41 +0300 Subject: [PATCH 07/34] feat(preprints-resources-filters-state): Implemented state --- .../preprints-resources-filters/index.ts | 4 + .../preprints-resources-filters.actions.ts | 45 ++++++++++ .../preprints-resources-filters.model.ts | 9 ++ .../preprints-resources-filters.selectors.ts | 40 +++++++++ .../preprints-resources-filters.state.ts | 83 +++++++++++++++++++ 5 files changed, 181 insertions(+) create mode 100644 src/app/features/preprints/store/preprints-resources-filters/index.ts create mode 100644 src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.actions.ts create mode 100644 src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.model.ts create mode 100644 src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.selectors.ts create mode 100644 src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.state.ts diff --git a/src/app/features/preprints/store/preprints-resources-filters/index.ts b/src/app/features/preprints/store/preprints-resources-filters/index.ts new file mode 100644 index 000000000..c8e42ec6e --- /dev/null +++ b/src/app/features/preprints/store/preprints-resources-filters/index.ts @@ -0,0 +1,4 @@ +export * from './preprints-resources-filters.actions'; +export * from './preprints-resources-filters.model'; +export * from './preprints-resources-filters.selectors'; +export * from './preprints-resources-filters.state'; diff --git a/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.actions.ts b/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.actions.ts new file mode 100644 index 000000000..35f265c25 --- /dev/null +++ b/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.actions.ts @@ -0,0 +1,45 @@ +export class SetCreator { + static readonly type = '[Preprints Resource Filters] Set Creator'; + + constructor( + public name: string, + public id: string + ) {} +} + +export class SetDateCreated { + static readonly type = '[Preprints Resource Filters] Set DateCreated'; + + constructor(public date: string) {} +} + +export class SetSubject { + static readonly type = '[Preprints Resource Filters] Set Subject'; + + constructor( + public subject: string, + public id: string + ) {} +} + +export class SetLicense { + static readonly type = '[Preprints Resource Filters] Set License'; + + constructor( + public license: string, + public id: string + ) {} +} + +export class SetProvider { + static readonly type = '[Preprints Resource Filters] Set Provider'; + + constructor( + public provider: string, + public id: string + ) {} +} + +export class ResetFiltersState { + static readonly type = '[Preprints Resource Filters] Reset State'; +} diff --git a/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.model.ts b/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.model.ts new file mode 100644 index 000000000..f5f236e1a --- /dev/null +++ b/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.model.ts @@ -0,0 +1,9 @@ +import { ResourceFilterLabel } from '@shared/models'; + +export interface PreprintsResourcesFiltersStateModel { + creator: ResourceFilterLabel; + dateCreated: ResourceFilterLabel; + subject: ResourceFilterLabel; + license: ResourceFilterLabel; + provider: ResourceFilterLabel; +} diff --git a/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.selectors.ts b/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.selectors.ts new file mode 100644 index 000000000..2dec1f355 --- /dev/null +++ b/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.selectors.ts @@ -0,0 +1,40 @@ +import { Selector } from '@ngxs/store'; + +import { ResourceFilterLabel } from '@shared/models'; + +import { PreprintsResourcesFiltersStateModel } from './preprints-resources-filters.model'; +import { PreprintsResourcesFiltersState } from './preprints-resources-filters.state'; + +export class PreprintsResourcesFiltersSelectors { + @Selector([PreprintsResourcesFiltersState]) + static getAllFilters(state: PreprintsResourcesFiltersStateModel): PreprintsResourcesFiltersStateModel { + return { + ...state, + }; + } + + @Selector([PreprintsResourcesFiltersState]) + static getCreator(state: PreprintsResourcesFiltersStateModel): ResourceFilterLabel { + return state.creator; + } + + @Selector([PreprintsResourcesFiltersState]) + static getDateCreated(state: PreprintsResourcesFiltersStateModel): ResourceFilterLabel { + return state.dateCreated; + } + + @Selector([PreprintsResourcesFiltersState]) + static getSubject(state: PreprintsResourcesFiltersStateModel): ResourceFilterLabel { + return state.subject; + } + + @Selector([PreprintsResourcesFiltersState]) + static getLicense(state: PreprintsResourcesFiltersStateModel): ResourceFilterLabel { + return state.license; + } + + @Selector([PreprintsResourcesFiltersState]) + static getProvider(state: PreprintsResourcesFiltersStateModel): ResourceFilterLabel { + return state.provider; + } +} diff --git a/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.state.ts b/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.state.ts new file mode 100644 index 000000000..7e823e43d --- /dev/null +++ b/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.state.ts @@ -0,0 +1,83 @@ +import { Action, State, StateContext } from '@ngxs/store'; + +import { Injectable } from '@angular/core'; + +import { FilterLabelsModel } from '@osf/shared/models'; +import { resourceFiltersDefaults } from '@shared/constants'; + +import { + ResetFiltersState, + SetCreator, + SetDateCreated, + SetLicense, + SetProvider, + SetSubject, +} from './preprints-resources-filters.actions'; +import { PreprintsResourcesFiltersStateModel } from './preprints-resources-filters.model'; + +@State({ + name: 'preprintsResourceFilters', + defaults: { ...resourceFiltersDefaults }, +}) +@Injectable() +export class PreprintsResourcesFiltersState { + @Action(SetCreator) + setCreator(ctx: StateContext, action: SetCreator) { + ctx.patchState({ + creator: { + filterName: FilterLabelsModel.creator, + label: action.name, + value: action.id, + }, + }); + } + + @Action(SetDateCreated) + setDateCreated(ctx: StateContext, action: SetDateCreated) { + ctx.patchState({ + dateCreated: { + filterName: FilterLabelsModel.dateCreated, + label: action.date, + value: action.date, + }, + }); + } + + @Action(SetSubject) + setSubject(ctx: StateContext, action: SetSubject) { + ctx.patchState({ + subject: { + filterName: FilterLabelsModel.subject, + label: action.subject, + value: action.id, + }, + }); + } + + @Action(SetLicense) + setLicense(ctx: StateContext, action: SetLicense) { + ctx.patchState({ + license: { + filterName: FilterLabelsModel.license, + label: action.license, + value: action.id, + }, + }); + } + + @Action(SetProvider) + setProvider(ctx: StateContext, action: SetProvider) { + ctx.patchState({ + provider: { + filterName: FilterLabelsModel.provider, + label: action.provider, + value: action.id, + }, + }); + } + + @Action(ResetFiltersState) + resetState(ctx: StateContext) { + ctx.patchState({ ...resourceFiltersDefaults }); + } +} From cc891338cb65278b53805d3e8ed1473a58c1c472 Mon Sep 17 00:00:00 2001 From: Roma Date: Tue, 10 Jun 2025 16:16:49 +0300 Subject: [PATCH 08/34] refactor(preprints-state): Moved to separate folder --- src/app/core/constants/ngxs-states.constant.ts | 2 +- .../preprints/pages/landing/preprints-landing.component.ts | 2 +- .../preprint-provider-discover.component.ts | 2 +- .../preprint-provider-overview.component.ts | 2 +- src/app/features/preprints/store/{ => preprints}/index.ts | 0 .../preprints/store/{ => preprints}/preprints.actions.ts | 0 .../preprints/store/{ => preprints}/preprints.model.ts | 0 .../preprints/store/{ => preprints}/preprints.selectors.ts | 2 +- .../preprints/store/{ => preprints}/preprints.state.ts | 5 +++-- 9 files changed, 8 insertions(+), 7 deletions(-) rename src/app/features/preprints/store/{ => preprints}/index.ts (100%) rename src/app/features/preprints/store/{ => preprints}/preprints.actions.ts (100%) rename src/app/features/preprints/store/{ => preprints}/preprints.model.ts (100%) rename src/app/features/preprints/store/{ => preprints}/preprints.selectors.ts (96%) rename src/app/features/preprints/store/{ => preprints}/preprints.state.ts (96%) diff --git a/src/app/core/constants/ngxs-states.constant.ts b/src/app/core/constants/ngxs-states.constant.ts index 26e99f4a2..062b1d5a5 100644 --- a/src/app/core/constants/ngxs-states.constant.ts +++ b/src/app/core/constants/ngxs-states.constant.ts @@ -4,7 +4,7 @@ import { CollectionsState } from '@osf/features/collections/store'; import { InstitutionsState } from '@osf/features/institutions/store'; import { MeetingsState } from '@osf/features/meetings/store'; import { MyProjectsState } from '@osf/features/my-projects/store'; -import { PreprintsState } from '@osf/features/preprints/store'; +import { PreprintsState } from '@osf/features/preprints/store/preprints'; import { AnalyticsState } from '@osf/features/project/analytics/store'; import { ProjectOverviewState } from '@osf/features/project/overview/store'; import { RegistrationsState } from '@osf/features/project/registrations/store'; diff --git a/src/app/features/preprints/pages/landing/preprints-landing.component.ts b/src/app/features/preprints/pages/landing/preprints-landing.component.ts index 3175b3088..4eaa63df4 100644 --- a/src/app/features/preprints/pages/landing/preprints-landing.component.ts +++ b/src/app/features/preprints/pages/landing/preprints-landing.component.ts @@ -20,7 +20,7 @@ import { GetPreprintProviderById, GetPreprintProvidersToAdvertise, PreprintsSelectors, -} from '@osf/features/preprints/store'; +} from '@osf/features/preprints/store/preprints'; import { SearchInputComponent } from '@shared/components'; import { ResourceTab } from '@shared/enums'; diff --git a/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.ts b/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.ts index 9d3b0a692..1dac0071f 100644 --- a/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.ts +++ b/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.ts @@ -8,7 +8,7 @@ import { ActivatedRoute } from '@angular/router'; import { PreprintProviderHeroComponent } from '@osf/features/preprints/components/preprint-provider-hero/preprint-provider-hero.component'; import { BrandService } from '@osf/features/preprints/services'; -import { GetPreprintProviderById, PreprintsSelectors } from '@osf/features/preprints/store'; +import { GetPreprintProviderById, PreprintsSelectors } from '@osf/features/preprints/store/preprints'; import { HeaderStyleHelper } from '@shared/utils'; @Component({ diff --git a/src/app/features/preprints/pages/preprint-provider-overview/preprint-provider-overview.component.ts b/src/app/features/preprints/pages/preprint-provider-overview/preprint-provider-overview.component.ts index 1b749066b..ade20c7e1 100644 --- a/src/app/features/preprints/pages/preprint-provider-overview/preprint-provider-overview.component.ts +++ b/src/app/features/preprints/pages/preprint-provider-overview/preprint-provider-overview.component.ts @@ -14,7 +14,7 @@ import { GetHighlightedSubjectsByProviderId, GetPreprintProviderById, PreprintsSelectors, -} from '@osf/features/preprints/store'; +} from '@osf/features/preprints/store/preprints'; import { HeaderStyleHelper } from '@shared/utils'; @Component({ diff --git a/src/app/features/preprints/store/index.ts b/src/app/features/preprints/store/preprints/index.ts similarity index 100% rename from src/app/features/preprints/store/index.ts rename to src/app/features/preprints/store/preprints/index.ts diff --git a/src/app/features/preprints/store/preprints.actions.ts b/src/app/features/preprints/store/preprints/preprints.actions.ts similarity index 100% rename from src/app/features/preprints/store/preprints.actions.ts rename to src/app/features/preprints/store/preprints/preprints.actions.ts diff --git a/src/app/features/preprints/store/preprints.model.ts b/src/app/features/preprints/store/preprints/preprints.model.ts similarity index 100% rename from src/app/features/preprints/store/preprints.model.ts rename to src/app/features/preprints/store/preprints/preprints.model.ts diff --git a/src/app/features/preprints/store/preprints.selectors.ts b/src/app/features/preprints/store/preprints/preprints.selectors.ts similarity index 96% rename from src/app/features/preprints/store/preprints.selectors.ts rename to src/app/features/preprints/store/preprints/preprints.selectors.ts index 4e3732f72..4af549b59 100644 --- a/src/app/features/preprints/store/preprints.selectors.ts +++ b/src/app/features/preprints/store/preprints/preprints.selectors.ts @@ -1,6 +1,6 @@ import { Selector } from '@ngxs/store'; -import { PreprintsState, PreprintsStateModel } from '@osf/features/preprints/store'; +import { PreprintsState, PreprintsStateModel } from '@osf/features/preprints/store/preprints/index'; export class PreprintsSelectors { @Selector([PreprintsState]) diff --git a/src/app/features/preprints/store/preprints.state.ts b/src/app/features/preprints/store/preprints/preprints.state.ts similarity index 96% rename from src/app/features/preprints/store/preprints.state.ts rename to src/app/features/preprints/store/preprints/preprints.state.ts index 261f65a61..45cc5dba1 100644 --- a/src/app/features/preprints/store/preprints.state.ts +++ b/src/app/features/preprints/store/preprints/preprints.state.ts @@ -11,8 +11,9 @@ import { GetHighlightedSubjectsByProviderId, GetPreprintProviderById, GetPreprintProvidersToAdvertise, -} from '@osf/features/preprints/store/preprints.actions'; -import { PreprintsStateModel } from '@osf/features/preprints/store/preprints.model'; +} from '@osf/features/preprints/store/preprints/preprints.actions'; + +import { PreprintsStateModel } from './'; @State({ name: 'preprints', From 52ff47df702ec9a44f3478c5f58a9e6e99f4684a Mon Sep 17 00:00:00 2001 From: Roma Date: Tue, 10 Jun 2025 17:17:45 +0300 Subject: [PATCH 09/34] fix(shared-const-index): Fixed index file --- .../resources-wrapper/resources-wrapper.component.ts | 5 ++--- src/app/shared/constants/index.ts | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/features/search/components/resources-wrapper/resources-wrapper.component.ts b/src/app/features/search/components/resources-wrapper/resources-wrapper.component.ts index ddfe54c2b..4939bf518 100644 --- a/src/app/features/search/components/resources-wrapper/resources-wrapper.component.ts +++ b/src/app/features/search/components/resources-wrapper/resources-wrapper.component.ts @@ -5,13 +5,13 @@ import { take } from 'rxjs'; import { ChangeDetectionStrategy, Component, effect, inject, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; +import { ResourcesComponent } from '@osf/features/search/components'; import { ResourceTab } from '@osf/shared/enums'; -import { FilterLabelsModel } from '@osf/shared/models'; +import { FilterLabelsModel, ResourceFilterLabel } from '@osf/shared/models'; import { SearchSelectors, SetResourceTab, SetSearchText, SetSortBy } from '../../store'; import { GetAllOptions } from '../filters/store'; import { - ResourceFilterLabel, ResourceFiltersSelectors, SetCreator, SetDateCreated, @@ -23,7 +23,6 @@ import { SetResourceType, SetSubject, } from '../resource-filters/store'; -import { ResourcesComponent } from '../resources/resources.component'; @Component({ selector: 'osf-resources-wrapper', diff --git a/src/app/shared/constants/index.ts b/src/app/shared/constants/index.ts index aed11d166..ec4e32867 100644 --- a/src/app/shared/constants/index.ts +++ b/src/app/shared/constants/index.ts @@ -2,4 +2,5 @@ export * from './input-limits.const'; export * from './input-validation-messages.const'; export * from './remove-nullable.const'; export * from './resource-filters-defaults'; +export * from './scientists.const'; export * from './search-state-defaults.const'; From 278fa35a38ae2ebd1a2c15a58165e15a041245db Mon Sep 17 00:00:00 2001 From: Roma Date: Tue, 10 Jun 2025 17:44:45 +0300 Subject: [PATCH 10/34] feat(preprints-search-state): Implemented state for preprints discover page --- .../preprints/store/preprints-search/index.ts | 4 + .../preprints-search.actions.ts | 29 +++++ .../preprints-search.model.ts | 11 ++ .../preprints-search.selectors.ts | 42 +++++++ .../preprints-search.state.ts | 113 ++++++++++++++++++ 5 files changed, 199 insertions(+) create mode 100644 src/app/features/preprints/store/preprints-search/index.ts create mode 100644 src/app/features/preprints/store/preprints-search/preprints-search.actions.ts create mode 100644 src/app/features/preprints/store/preprints-search/preprints-search.model.ts create mode 100644 src/app/features/preprints/store/preprints-search/preprints-search.selectors.ts create mode 100644 src/app/features/preprints/store/preprints-search/preprints-search.state.ts diff --git a/src/app/features/preprints/store/preprints-search/index.ts b/src/app/features/preprints/store/preprints-search/index.ts new file mode 100644 index 000000000..777222be7 --- /dev/null +++ b/src/app/features/preprints/store/preprints-search/index.ts @@ -0,0 +1,4 @@ +export * from './preprints-search.actions'; +export * from './preprints-search.model'; +export * from './preprints-search.selectors'; +export * from './preprints-search.state'; diff --git a/src/app/features/preprints/store/preprints-search/preprints-search.actions.ts b/src/app/features/preprints/store/preprints-search/preprints-search.actions.ts new file mode 100644 index 000000000..d6096ee82 --- /dev/null +++ b/src/app/features/preprints/store/preprints-search/preprints-search.actions.ts @@ -0,0 +1,29 @@ +export class GetResources { + static readonly type = '[Preprints search] Get Resources'; +} + +export class GetResourcesByLink { + static readonly type = '[Preprints search] Get Resources By Link'; + + constructor(public link: string) {} +} + +export class GetResourcesCount { + static readonly type = '[Preprints search] Get Resources Count'; +} + +export class SetSearchText { + static readonly type = '[Preprints search] Set Search Text'; + + constructor(public searchText: string) {} +} + +export class SetSortBy { + static readonly type = '[Preprints search] Set SortBy'; + + constructor(public sortBy: string) {} +} + +export class ResetState { + static readonly type = '[Preprints search] Reset State'; +} diff --git a/src/app/features/preprints/store/preprints-search/preprints-search.model.ts b/src/app/features/preprints/store/preprints-search/preprints-search.model.ts new file mode 100644 index 000000000..84748db6a --- /dev/null +++ b/src/app/features/preprints/store/preprints-search/preprints-search.model.ts @@ -0,0 +1,11 @@ +import { AsyncStateModel, Resource } from '@osf/shared/models'; + +export interface PreprintsSearchStateModel { + resources: AsyncStateModel; + resourcesCount: number; + searchText: string; + sortBy: string; + first: string; + next: string; + previous: string; +} diff --git a/src/app/features/preprints/store/preprints-search/preprints-search.selectors.ts b/src/app/features/preprints/store/preprints-search/preprints-search.selectors.ts new file mode 100644 index 000000000..774d827ee --- /dev/null +++ b/src/app/features/preprints/store/preprints-search/preprints-search.selectors.ts @@ -0,0 +1,42 @@ +import { Selector } from '@ngxs/store'; + +import { Resource } from '@osf/shared/models/resource-card/resource.model'; + +import { PreprintsSearchState, PreprintsSearchStateModel } from './'; + +export class PreprintsSearchSelectors { + @Selector([PreprintsSearchState]) + static getResources(state: PreprintsSearchStateModel): Resource[] { + return state.resources.data; + } + + @Selector([PreprintsSearchState]) + static getResourcesCount(state: PreprintsSearchStateModel): number { + return state.resourcesCount; + } + + @Selector([PreprintsSearchState]) + static getSearchText(state: PreprintsSearchStateModel): string { + return state.searchText; + } + + @Selector([PreprintsSearchState]) + static getSortBy(state: PreprintsSearchStateModel): string { + return state.sortBy; + } + + @Selector([PreprintsSearchState]) + static getFirst(state: PreprintsSearchStateModel): string { + return state.first; + } + + @Selector([PreprintsSearchState]) + static getNext(state: PreprintsSearchStateModel): string { + return state.next; + } + + @Selector([PreprintsSearchState]) + static getPrevious(state: PreprintsSearchStateModel): string { + return state.previous; + } +} diff --git a/src/app/features/preprints/store/preprints-search/preprints-search.state.ts b/src/app/features/preprints/store/preprints-search/preprints-search.state.ts new file mode 100644 index 000000000..6dff68dbf --- /dev/null +++ b/src/app/features/preprints/store/preprints-search/preprints-search.state.ts @@ -0,0 +1,113 @@ +import { Action, NgxsOnInit, State, StateContext, Store } from '@ngxs/store'; + +import { BehaviorSubject, EMPTY, switchMap, tap } from 'rxjs'; + +import { inject, Injectable } from '@angular/core'; + +import { PreprintsResourcesFiltersSelectors } from '@osf/features/preprints/store/preprints-resources-filters'; +import { + GetResources, + GetResourcesByLink, + PreprintsSearchSelectors, + ResetState, + SetSearchText, + SetSortBy, +} from '@osf/features/preprints/store/preprints-search'; +import { ResourceFiltersStateModel } from '@osf/features/search/components/resource-filters/store'; +import { SearchService } from '@osf/shared/services'; +import { addFiltersParams, getResourceTypes } from '@osf/shared/utils'; +import { searchStateDefaults } from '@shared/constants'; +import { GetResourcesRequestTypeEnum, ResourceTab } from '@shared/enums'; + +import { PreprintsSearchStateModel } from './preprints-search.model'; + +@Injectable() +@State({ + name: 'preprintsSearch', + defaults: searchStateDefaults, +}) +export class PreprintsSearchState implements NgxsOnInit { + searchService = inject(SearchService); + store = inject(Store); + loadRequests = new BehaviorSubject<{ type: GetResourcesRequestTypeEnum; link?: string } | null>(null); + + ngxsOnInit(ctx: StateContext): void { + this.loadRequests + .pipe( + switchMap((query) => { + if (!query) return EMPTY; + const state = ctx.getState(); + ctx.patchState({ resources: { ...state.resources, isLoading: true } }); + if (query.type === GetResourcesRequestTypeEnum.GetResources) { + const filters = this.store.selectSnapshot(PreprintsResourcesFiltersSelectors.getAllFilters); + const filtersParams = addFiltersParams(filters as ResourceFiltersStateModel); + const searchText = this.store.selectSnapshot(PreprintsSearchSelectors.getSearchText); + const sortBy = this.store.selectSnapshot(PreprintsSearchSelectors.getSortBy); + const resourceTab = ResourceTab.Preprints; + const resourceTypes = getResourceTypes(resourceTab); + + return this.searchService.getResources(filtersParams, searchText, sortBy, resourceTypes).pipe( + tap((response) => { + ctx.patchState({ resources: { data: response.resources, isLoading: false, error: null } }); + ctx.patchState({ resourcesCount: response.count }); + ctx.patchState({ first: response.first }); + ctx.patchState({ next: response.next }); + ctx.patchState({ previous: response.previous }); + }) + ); + } else if (query.type === GetResourcesRequestTypeEnum.GetResourcesByLink) { + if (query.link) { + return this.searchService.getResourcesByLink(query.link!).pipe( + tap((response) => { + ctx.patchState({ + resources: { + data: response.resources, + isLoading: false, + error: null, + }, + }); + ctx.patchState({ resourcesCount: response.count }); + ctx.patchState({ first: response.first }); + ctx.patchState({ next: response.next }); + ctx.patchState({ previous: response.previous }); + }) + ); + } + return EMPTY; + } + return EMPTY; + }) + ) + .subscribe(); + } + + @Action(GetResources) + getResources() { + this.loadRequests.next({ + type: GetResourcesRequestTypeEnum.GetResources, + }); + } + + @Action(GetResourcesByLink) + getResourcesByLink(ctx: StateContext, action: GetResourcesByLink) { + this.loadRequests.next({ + type: GetResourcesRequestTypeEnum.GetResourcesByLink, + link: action.link, + }); + } + + @Action(SetSearchText) + setSearchText(ctx: StateContext, action: SetSearchText) { + ctx.patchState({ searchText: action.searchText }); + } + + @Action(SetSortBy) + setSortBy(ctx: StateContext, action: SetSortBy) { + ctx.patchState({ sortBy: action.sortBy }); + } + + @Action(ResetState) + resetState(ctx: StateContext) { + ctx.patchState(searchStateDefaults); + } +} From 58b641a604b22eacd72a2fea692fb77f5b3f6315 Mon Sep 17 00:00:00 2001 From: Roma Date: Tue, 10 Jun 2025 18:14:49 +0300 Subject: [PATCH 11/34] feat(preprints-resources-filters-options): Implemented state for preprints resources filters options --- .../core/constants/ngxs-states.constant.ts | 6 ++ src/app/features/preprints/services/index.ts | 1 + .../preprints-resource-filters.service.ts | 60 ++++++++++++ .../index.ts | 4 + ...rints-resources-filters-options.actions.ts | 25 +++++ ...eprints-resources-filters-options.model.ts | 9 ++ ...nts-resources-filters-options.selectors.ts | 40 ++++++++ ...eprints-resources-filters-options.state.ts | 91 +++++++++++++++++++ 8 files changed, 236 insertions(+) create mode 100644 src/app/features/preprints/services/preprints-resource-filters.service.ts create mode 100644 src/app/features/preprints/store/preprints-resources-filters-options/index.ts create mode 100644 src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.actions.ts create mode 100644 src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.model.ts create mode 100644 src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.selectors.ts create mode 100644 src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.state.ts diff --git a/src/app/core/constants/ngxs-states.constant.ts b/src/app/core/constants/ngxs-states.constant.ts index 062b1d5a5..ac13acd7e 100644 --- a/src/app/core/constants/ngxs-states.constant.ts +++ b/src/app/core/constants/ngxs-states.constant.ts @@ -5,6 +5,9 @@ import { InstitutionsState } from '@osf/features/institutions/store'; import { MeetingsState } from '@osf/features/meetings/store'; import { MyProjectsState } from '@osf/features/my-projects/store'; import { PreprintsState } from '@osf/features/preprints/store/preprints'; +import { PreprintsResourcesFiltersState } from '@osf/features/preprints/store/preprints-resources-filters'; +import { PreprintsResourcesFiltersOptionsState } from '@osf/features/preprints/store/preprints-resources-filters-options'; +import { PreprintsSearchState } from '@osf/features/preprints/store/preprints-search'; import { AnalyticsState } from '@osf/features/project/analytics/store'; import { ProjectOverviewState } from '@osf/features/project/overview/store'; import { RegistrationsState } from '@osf/features/project/registrations/store'; @@ -36,4 +39,7 @@ export const STATES = [ MeetingsState, RegistrationsState, PreprintsState, + PreprintsSearchState, + PreprintsResourcesFiltersState, + PreprintsResourcesFiltersOptionsState, ]; diff --git a/src/app/features/preprints/services/index.ts b/src/app/features/preprints/services/index.ts index fd2d1dc28..d93e065fa 100644 --- a/src/app/features/preprints/services/index.ts +++ b/src/app/features/preprints/services/index.ts @@ -1,2 +1,3 @@ export { BrandService } from './brand.service'; export { PreprintsService } from './preprints.service'; +export { PreprintsFiltersOptionsService } from './preprints-resource-filters.service'; diff --git a/src/app/features/preprints/services/preprints-resource-filters.service.ts b/src/app/features/preprints/services/preprints-resource-filters.service.ts new file mode 100644 index 000000000..77031d7e2 --- /dev/null +++ b/src/app/features/preprints/services/preprints-resource-filters.service.ts @@ -0,0 +1,60 @@ +import { select, Store } from '@ngxs/store'; + +import { Observable } from 'rxjs'; + +import { inject, Injectable } from '@angular/core'; + +import { PreprintsResourcesFiltersSelectors } from '@osf/features/preprints/store/preprints-resources-filters'; +import { PreprintsSearchSelectors } from '@osf/features/preprints/store/preprints-search'; +import { ResourceFiltersStateModel } from '@osf/features/search/components/resource-filters/store'; +import { Creator, DateCreated, LicenseFilter, ProviderFilter, SubjectFilter } from '@osf/shared/models'; +import { FiltersOptionsService } from '@osf/shared/services'; +import { addFiltersParams, getResourceTypes } from '@osf/shared/utils'; +import { ResourceTab } from '@shared/enums'; + +@Injectable({ + providedIn: 'root', +}) +export class PreprintsFiltersOptionsService { + #store = inject(Store); + #filtersOptions = inject(FiltersOptionsService); + + #getFilterParams(): Record { + return addFiltersParams(select(PreprintsResourcesFiltersSelectors.getAllFilters)() as ResourceFiltersStateModel); + } + + #getParams(): Record { + const params: Record = {}; + const resourceTab = ResourceTab.Preprints; + const resourceTypes = getResourceTypes(resourceTab); + const searchText = this.#store.selectSnapshot(PreprintsSearchSelectors.getSearchText); + const sort = this.#store.selectSnapshot(PreprintsSearchSelectors.getSortBy); + + params['cardSearchFilter[resourceType]'] = resourceTypes; + params['cardSearchFilter[accessService]'] = 'https://staging4.osf.io/'; + params['cardSearchText[*,creator.name,isContainedBy.creator.name]'] = searchText; + params['page[size]'] = '10'; + params['sort'] = sort; + return params; + } + + getCreators(valueSearchText: string): Observable { + return this.#filtersOptions.getCreators(valueSearchText, this.#getParams(), this.#getFilterParams()); + } + + getDates(): Observable { + return this.#filtersOptions.getDates(this.#getParams(), this.#getFilterParams()); + } + + getSubjects(): Observable { + return this.#filtersOptions.getSubjects(this.#getParams(), this.#getFilterParams()); + } + + getLicenses(): Observable { + return this.#filtersOptions.getLicenses(this.#getParams(), this.#getFilterParams()); + } + + getProviders(): Observable { + return this.#filtersOptions.getProviders(this.#getParams(), this.#getFilterParams()); + } +} diff --git a/src/app/features/preprints/store/preprints-resources-filters-options/index.ts b/src/app/features/preprints/store/preprints-resources-filters-options/index.ts new file mode 100644 index 000000000..c8dc317d6 --- /dev/null +++ b/src/app/features/preprints/store/preprints-resources-filters-options/index.ts @@ -0,0 +1,4 @@ +export * from './preprints-resources-filters-options.actions'; +export * from './preprints-resources-filters-options.model'; +export * from './preprints-resources-filters-options.selectors'; +export * from './preprints-resources-filters-options.state'; diff --git a/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.actions.ts b/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.actions.ts new file mode 100644 index 000000000..f3db28408 --- /dev/null +++ b/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.actions.ts @@ -0,0 +1,25 @@ +export class GetCreatorsOptions { + static readonly type = '[Preprints Resource Filters Options] Get Creators'; + + constructor(public searchName: string) {} +} + +export class GetDatesCreatedOptions { + static readonly type = '[Preprints Resource Filters Options] Get Dates Created'; +} + +export class GetSubjectsOptions { + static readonly type = '[Preprints Resource Filters Options] Get Subjects'; +} + +export class GetLicensesOptions { + static readonly type = '[Preprints Resource Filters Options] Get Licenses'; +} + +export class GetProvidersOptions { + static readonly type = '[Preprints Resource Filters Options] Get Providers'; +} + +export class GetAllOptions { + static readonly type = '[Preprints Resource Filters Options] Get All Options'; +} diff --git a/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.model.ts b/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.model.ts new file mode 100644 index 000000000..5a262f47b --- /dev/null +++ b/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.model.ts @@ -0,0 +1,9 @@ +import { Creator, DateCreated, LicenseFilter, ProviderFilter, SubjectFilter } from '@osf/shared/models'; + +export interface PreprintsResourceFiltersOptionsStateModel { + creators: Creator[]; + datesCreated: DateCreated[]; + subjects: SubjectFilter[]; + licenses: LicenseFilter[]; + providers: ProviderFilter[]; +} diff --git a/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.selectors.ts b/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.selectors.ts new file mode 100644 index 000000000..6f24d9fb6 --- /dev/null +++ b/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.selectors.ts @@ -0,0 +1,40 @@ +import { Selector } from '@ngxs/store'; + +import { Creator, DateCreated, LicenseFilter, ProviderFilter, SubjectFilter } from '@osf/shared/models'; + +import { PreprintsResourceFiltersOptionsStateModel } from './preprints-resources-filters-options.model'; +import { PreprintsResourcesFiltersOptionsState } from './preprints-resources-filters-options.state'; + +export class PreprintsResourcesFiltersOptionsSelectors { + @Selector([PreprintsResourcesFiltersOptionsState]) + static getCreators(state: PreprintsResourceFiltersOptionsStateModel): Creator[] { + return state.creators; + } + + @Selector([PreprintsResourcesFiltersOptionsState]) + static getDatesCreated(state: PreprintsResourceFiltersOptionsStateModel): DateCreated[] { + return state.datesCreated; + } + + @Selector([PreprintsResourcesFiltersOptionsState]) + static getSubjects(state: PreprintsResourceFiltersOptionsStateModel): SubjectFilter[] { + return state.subjects; + } + + @Selector([PreprintsResourcesFiltersOptionsState]) + static getLicenses(state: PreprintsResourceFiltersOptionsStateModel): LicenseFilter[] { + return state.licenses; + } + + @Selector([PreprintsResourcesFiltersOptionsState]) + static getProviders(state: PreprintsResourceFiltersOptionsStateModel): ProviderFilter[] { + return state.providers; + } + + @Selector([PreprintsResourcesFiltersOptionsState]) + static getAllOptions(state: PreprintsResourceFiltersOptionsStateModel): PreprintsResourceFiltersOptionsStateModel { + return { + ...state, + }; + } +} diff --git a/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.state.ts b/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.state.ts new file mode 100644 index 000000000..534491389 --- /dev/null +++ b/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.state.ts @@ -0,0 +1,91 @@ +import { Action, State, StateContext, Store } from '@ngxs/store'; + +import { tap } from 'rxjs'; + +import { inject, Injectable } from '@angular/core'; + +import { PreprintsFiltersOptionsService } from '@osf/features/preprints/services'; + +import { + GetAllOptions, + GetCreatorsOptions, + GetDatesCreatedOptions, + GetLicensesOptions, + GetProvidersOptions, + GetSubjectsOptions, +} from './preprints-resources-filters-options.actions'; +import { PreprintsResourceFiltersOptionsStateModel } from './preprints-resources-filters-options.model'; + +@State({ + name: 'preprintsResourceFiltersOptions', + defaults: { + creators: [], + datesCreated: [], + subjects: [], + licenses: [], + providers: [], + }, +}) +@Injectable() +export class PreprintsResourcesFiltersOptionsState { + readonly store = inject(Store); + readonly resourceFiltersService = inject(PreprintsFiltersOptionsService); + + @Action(GetCreatorsOptions) + getCreatorsOptions(ctx: StateContext, action: GetCreatorsOptions) { + if (!action.searchName) { + ctx.patchState({ creators: [] }); + return []; + } + + return this.resourceFiltersService.getCreators(action.searchName).pipe( + tap((creators) => { + ctx.patchState({ creators: creators }); + }) + ); + } + + @Action(GetDatesCreatedOptions) + getDatesCreated(ctx: StateContext) { + return this.resourceFiltersService.getDates().pipe( + tap((datesCreated) => { + ctx.patchState({ datesCreated: datesCreated }); + }) + ); + } + + @Action(GetSubjectsOptions) + getSubjects(ctx: StateContext) { + return this.resourceFiltersService.getSubjects().pipe( + tap((subjects) => { + ctx.patchState({ subjects: subjects }); + }) + ); + } + + @Action(GetLicensesOptions) + getLicenses(ctx: StateContext) { + return this.resourceFiltersService.getLicenses().pipe( + tap((licenses) => { + ctx.patchState({ licenses: licenses }); + }) + ); + } + + @Action(GetProvidersOptions) + getProviders(ctx: StateContext) { + return this.resourceFiltersService.getProviders().pipe( + tap((providers) => { + ctx.patchState({ providers: providers }); + }) + ); + } + + @Action(GetAllOptions) + getAllOptions() { + this.store.dispatch(GetDatesCreatedOptions); + this.store.dispatch(GetSubjectsOptions); + this.store.dispatch(GetLicensesOptions); + this.store.dispatch(GetProvidersOptions); + } +} From 7709a5478d3ea5db6fc6782c55b40493ae7228c7 Mon Sep 17 00:00:00 2001 From: Roma Date: Tue, 10 Jun 2025 21:05:26 +0300 Subject: [PATCH 12/34] feat(preprints-filters-chips): Implemented filters chips component --- .../preprints-filter-chips.component.html | 74 +++++++++++++++++++ .../preprints-filter-chips.component.scss | 15 ++++ .../preprints-filter-chips.component.spec.ts | 22 ++++++ .../preprints-filter-chips.component.ts | 60 +++++++++++++++ 4 files changed, 171 insertions(+) create mode 100644 src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.html create mode 100644 src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.scss create mode 100644 src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.spec.ts create mode 100644 src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.ts diff --git a/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.html b/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.html new file mode 100644 index 000000000..d4c362145 --- /dev/null +++ b/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.html @@ -0,0 +1,74 @@ +@if (filters().creator.value) { + @let creator = filters().creator.filterName + ': ' + filters().creator.label; + + + + + +} + +@if (filters().dateCreated.value) { + @let dateCreated = filters().dateCreated.filterName + ': ' + filters().dateCreated.label; + + + + + +} + +@if (filters().subject.value) { + @let subject = filters().subject.filterName + ': ' + filters().subject.label; + + + + + +} + +@if (filters().license.value) { + @let license = filters().license.filterName + ': ' + filters().license.label; + + + + + +} + +@if (filters().provider.value) { + @let provider = filters().provider.filterName + ': ' + filters().provider.label; + + + + + +} diff --git a/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.scss b/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.scss new file mode 100644 index 000000000..ecd03d738 --- /dev/null +++ b/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.scss @@ -0,0 +1,15 @@ +:host { + display: flex; + flex-direction: column; + gap: 0.4rem; + + @media (max-width: 1200px) { + flex-direction: row; + } + + @media (max-width: 600px) { + flex-direction: column; + } +} + +//TODO diff --git a/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.spec.ts b/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.spec.ts new file mode 100644 index 000000000..aaa162f12 --- /dev/null +++ b/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PreprintsFilterChipsComponent } from './preprints-filter-chips.component'; + +describe('PreprintsFilterChipsComponent', () => { + let component: PreprintsFilterChipsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PreprintsFilterChipsComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(PreprintsFilterChipsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.ts b/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.ts new file mode 100644 index 000000000..047a747de --- /dev/null +++ b/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.ts @@ -0,0 +1,60 @@ +import { createDispatchMap, select } from '@ngxs/store'; + +import { Chip } from 'primeng/chip'; + +import { ChangeDetectionStrategy, Component } from '@angular/core'; + +import { + PreprintsResourcesFiltersSelectors, + SetProvider, +} from '@osf/features/preprints/store/preprints-resources-filters'; +import { GetAllOptions } from '@osf/features/preprints/store/preprints-resources-filters-options'; +import { + SetCreator, + SetDateCreated, + SetLicense, + SetSubject, +} from '@osf/features/search/components/resource-filters/store'; +import { FilterType } from '@osf/shared/enums'; + +@Component({ + selector: 'osf-preprints-filter-chips', + imports: [Chip], + templateUrl: './preprints-filter-chips.component.html', + styleUrl: './preprints-filter-chips.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class PreprintsFilterChipsComponent { + protected readonly FilterType = FilterType; + private readonly actions = createDispatchMap({ + setCreator: SetCreator, + setDateCreated: SetDateCreated, + setSubject: SetSubject, + setLicense: SetLicense, + setProvider: SetProvider, + getAllOptions: GetAllOptions, + }); + + filters = select(PreprintsResourcesFiltersSelectors.getAllFilters); + + clearFilter(filter: FilterType) { + switch (filter) { + case FilterType.Creator: + this.actions.setCreator('', ''); + break; + case FilterType.DateCreated: + this.actions.setDateCreated(''); + break; + case FilterType.Subject: + this.actions.setSubject('', ''); + break; + case FilterType.License: + this.actions.setLicense('', ''); + break; + case FilterType.Provider: + this.actions.setProvider('', ''); + break; + } + this.actions.getAllOptions(); + } +} From acb4c2e5ab85685a0730f7f2dce3f0e681a24752 Mon Sep 17 00:00:00 2001 From: Roma Date: Tue, 10 Jun 2025 21:06:03 +0300 Subject: [PATCH 13/34] feat(preprints-date-created-filter): Implemented filter component --- ...eprints-date-created-filter.component.html | 13 ++++ ...eprints-date-created-filter.component.scss | 0 ...ints-date-created-filter.component.spec.ts | 22 +++++++ ...preprints-date-created-filter.component.ts | 62 +++++++++++++++++++ 4 files changed, 97 insertions(+) create mode 100644 src/app/features/preprints/components/filters/preprints-date-created-filter/preprints-date-created-filter.component.html create mode 100644 src/app/features/preprints/components/filters/preprints-date-created-filter/preprints-date-created-filter.component.scss create mode 100644 src/app/features/preprints/components/filters/preprints-date-created-filter/preprints-date-created-filter.component.spec.ts create mode 100644 src/app/features/preprints/components/filters/preprints-date-created-filter/preprints-date-created-filter.component.ts diff --git a/src/app/features/preprints/components/filters/preprints-date-created-filter/preprints-date-created-filter.component.html b/src/app/features/preprints/components/filters/preprints-date-created-filter/preprints-date-created-filter.component.html new file mode 100644 index 000000000..b6188ece4 --- /dev/null +++ b/src/app/features/preprints/components/filters/preprints-date-created-filter/preprints-date-created-filter.component.html @@ -0,0 +1,13 @@ + diff --git a/src/app/features/preprints/components/filters/preprints-date-created-filter/preprints-date-created-filter.component.scss b/src/app/features/preprints/components/filters/preprints-date-created-filter/preprints-date-created-filter.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/features/preprints/components/filters/preprints-date-created-filter/preprints-date-created-filter.component.spec.ts b/src/app/features/preprints/components/filters/preprints-date-created-filter/preprints-date-created-filter.component.spec.ts new file mode 100644 index 000000000..440924c1a --- /dev/null +++ b/src/app/features/preprints/components/filters/preprints-date-created-filter/preprints-date-created-filter.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PreprintsDateCreatedFilterComponent } from './preprints-date-created-filter.component'; + +describe('PreprintsDateCreatedFilterComponent', () => { + let component: PreprintsDateCreatedFilterComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PreprintsDateCreatedFilterComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(PreprintsDateCreatedFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/preprints/components/filters/preprints-date-created-filter/preprints-date-created-filter.component.ts b/src/app/features/preprints/components/filters/preprints-date-created-filter/preprints-date-created-filter.component.ts new file mode 100644 index 000000000..5b7cc5445 --- /dev/null +++ b/src/app/features/preprints/components/filters/preprints-date-created-filter/preprints-date-created-filter.component.ts @@ -0,0 +1,62 @@ +import { createDispatchMap, select } from '@ngxs/store'; + +import { Select, SelectChangeEvent } from 'primeng/select'; + +import { ChangeDetectionStrategy, Component, computed, effect, signal, untracked } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { + PreprintsResourcesFiltersSelectors, + SetDateCreated, +} from '@osf/features/preprints/store/preprints-resources-filters'; +import { + GetAllOptions, + PreprintsResourcesFiltersOptionsSelectors, +} from '@osf/features/preprints/store/preprints-resources-filters-options'; + +@Component({ + selector: 'osf-preprints-date-created-filter', + imports: [Select, FormsModule], + templateUrl: './preprints-date-created-filter.component.html', + styleUrl: './preprints-date-created-filter.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class PreprintsDateCreatedFilterComponent { + private readonly actions = createDispatchMap({ + setDateCreated: SetDateCreated, + getAllOptions: GetAllOptions, + }); + + dateCreatedState = select(PreprintsResourcesFiltersSelectors.getDateCreated); + inputDate = signal(null); + + availableDates = select(PreprintsResourcesFiltersOptionsSelectors.getDatesCreated); + datesOptions = computed(() => { + return this.availableDates().map((date) => ({ + label: date.value + ' (' + date.count + ')', + value: date.value, + })); + }); + + constructor() { + effect(() => { + const storeValue = this.dateCreatedState().label; + const currentInput = untracked(() => this.inputDate()); + + if (!storeValue && currentInput !== null) { + this.inputDate.set(null); + } else if (storeValue && currentInput !== storeValue) { + this.inputDate.set(storeValue); + } + }); + } + + setDateCreated(event: SelectChangeEvent): void { + if (!(event.originalEvent as PointerEvent).pointerId) { + return; + } + + this.actions.setDateCreated(event.value); + this.actions.getAllOptions(); + } +} From eb4a80f200e944a2230242fd5954ddcb5f3c4a2e Mon Sep 17 00:00:00 2001 From: Roma Date: Tue, 10 Jun 2025 23:41:58 +0300 Subject: [PATCH 14/34] fix(preprints-search-state): Created new state to fix strange error --- .../preprints-resource-filters.service.ts | 24 +++---- .../store/preprints-discover/index.ts | 4 ++ .../preprints-discover.actions.ts | 25 ++++++++ .../preprints-discover.model.ts} | 4 +- .../preprints-discover.selectors.ts | 43 +++++++++++++ .../preprints-discover.state.ts} | 64 +++++++++++++------ .../preprints/store/preprints-search/index.ts | 4 -- .../preprints-search.actions.ts | 29 --------- .../preprints-search.selectors.ts | 42 ------------ 9 files changed, 129 insertions(+), 110 deletions(-) create mode 100644 src/app/features/preprints/store/preprints-discover/index.ts create mode 100644 src/app/features/preprints/store/preprints-discover/preprints-discover.actions.ts rename src/app/features/preprints/store/{preprints-search/preprints-search.model.ts => preprints-discover/preprints-discover.model.ts} (60%) create mode 100644 src/app/features/preprints/store/preprints-discover/preprints-discover.selectors.ts rename src/app/features/preprints/store/{preprints-search/preprints-search.state.ts => preprints-discover/preprints-discover.state.ts} (67%) delete mode 100644 src/app/features/preprints/store/preprints-search/index.ts delete mode 100644 src/app/features/preprints/store/preprints-search/preprints-search.actions.ts delete mode 100644 src/app/features/preprints/store/preprints-search/preprints-search.selectors.ts diff --git a/src/app/features/preprints/services/preprints-resource-filters.service.ts b/src/app/features/preprints/services/preprints-resource-filters.service.ts index 77031d7e2..b5d294b71 100644 --- a/src/app/features/preprints/services/preprints-resource-filters.service.ts +++ b/src/app/features/preprints/services/preprints-resource-filters.service.ts @@ -4,8 +4,8 @@ import { Observable } from 'rxjs'; import { inject, Injectable } from '@angular/core'; +import { PreprintsDiscoverSelectors } from '@osf/features/preprints/store/preprints-discover'; import { PreprintsResourcesFiltersSelectors } from '@osf/features/preprints/store/preprints-resources-filters'; -import { PreprintsSearchSelectors } from '@osf/features/preprints/store/preprints-search'; import { ResourceFiltersStateModel } from '@osf/features/search/components/resource-filters/store'; import { Creator, DateCreated, LicenseFilter, ProviderFilter, SubjectFilter } from '@osf/shared/models'; import { FiltersOptionsService } from '@osf/shared/services'; @@ -16,19 +16,19 @@ import { ResourceTab } from '@shared/enums'; providedIn: 'root', }) export class PreprintsFiltersOptionsService { - #store = inject(Store); - #filtersOptions = inject(FiltersOptionsService); + store = inject(Store); + filtersOptions = inject(FiltersOptionsService); - #getFilterParams(): Record { + private getFilterParams(): Record { return addFiltersParams(select(PreprintsResourcesFiltersSelectors.getAllFilters)() as ResourceFiltersStateModel); } - #getParams(): Record { + private getParams(): Record { const params: Record = {}; const resourceTab = ResourceTab.Preprints; const resourceTypes = getResourceTypes(resourceTab); - const searchText = this.#store.selectSnapshot(PreprintsSearchSelectors.getSearchText); - const sort = this.#store.selectSnapshot(PreprintsSearchSelectors.getSortBy); + const searchText = this.store.selectSnapshot(PreprintsDiscoverSelectors.getSearchText); + const sort = this.store.selectSnapshot(PreprintsDiscoverSelectors.getSortBy); params['cardSearchFilter[resourceType]'] = resourceTypes; params['cardSearchFilter[accessService]'] = 'https://staging4.osf.io/'; @@ -39,22 +39,22 @@ export class PreprintsFiltersOptionsService { } getCreators(valueSearchText: string): Observable { - return this.#filtersOptions.getCreators(valueSearchText, this.#getParams(), this.#getFilterParams()); + return this.filtersOptions.getCreators(valueSearchText, this.getParams(), this.getFilterParams()); } getDates(): Observable { - return this.#filtersOptions.getDates(this.#getParams(), this.#getFilterParams()); + return this.filtersOptions.getDates(this.getParams(), this.getFilterParams()); } getSubjects(): Observable { - return this.#filtersOptions.getSubjects(this.#getParams(), this.#getFilterParams()); + return this.filtersOptions.getSubjects(this.getParams(), this.getFilterParams()); } getLicenses(): Observable { - return this.#filtersOptions.getLicenses(this.#getParams(), this.#getFilterParams()); + return this.filtersOptions.getLicenses(this.getParams(), this.getFilterParams()); } getProviders(): Observable { - return this.#filtersOptions.getProviders(this.#getParams(), this.#getFilterParams()); + return this.filtersOptions.getProviders(this.getParams(), this.getFilterParams()); } } diff --git a/src/app/features/preprints/store/preprints-discover/index.ts b/src/app/features/preprints/store/preprints-discover/index.ts new file mode 100644 index 000000000..6e0281f9d --- /dev/null +++ b/src/app/features/preprints/store/preprints-discover/index.ts @@ -0,0 +1,4 @@ +export * from './preprints-discover.actions'; +export * from './preprints-discover.model'; +export * from './preprints-discover.selectors'; +export * from './preprints-discover.state'; diff --git a/src/app/features/preprints/store/preprints-discover/preprints-discover.actions.ts b/src/app/features/preprints/store/preprints-discover/preprints-discover.actions.ts new file mode 100644 index 000000000..8df945ce4 --- /dev/null +++ b/src/app/features/preprints/store/preprints-discover/preprints-discover.actions.ts @@ -0,0 +1,25 @@ +export class GetResources { + static readonly type = '[Preprints Discover] Get Resources'; +} + +export class GetResourcesByLink { + static readonly type = '[Preprints Discover] Get Resources By Link'; + + constructor(public link: string) {} +} + +export class SetSearchText { + static readonly type = '[Preprints Discover] Set Search Text'; + + constructor(public searchText: string) {} +} + +export class SetSortBy { + static readonly type = '[Preprints Discover] Set SortBy'; + + constructor(public sortBy: string) {} +} + +export class ResetState { + static readonly type = '[Preprints Discover] Reset State'; +} diff --git a/src/app/features/preprints/store/preprints-search/preprints-search.model.ts b/src/app/features/preprints/store/preprints-discover/preprints-discover.model.ts similarity index 60% rename from src/app/features/preprints/store/preprints-search/preprints-search.model.ts rename to src/app/features/preprints/store/preprints-discover/preprints-discover.model.ts index 84748db6a..15d628071 100644 --- a/src/app/features/preprints/store/preprints-search/preprints-search.model.ts +++ b/src/app/features/preprints/store/preprints-discover/preprints-discover.model.ts @@ -1,6 +1,6 @@ -import { AsyncStateModel, Resource } from '@osf/shared/models'; +import { AsyncStateModel, Resource } from '@shared/models'; -export interface PreprintsSearchStateModel { +export interface PreprintsDiscoverStateModel { resources: AsyncStateModel; resourcesCount: number; searchText: string; diff --git a/src/app/features/preprints/store/preprints-discover/preprints-discover.selectors.ts b/src/app/features/preprints/store/preprints-discover/preprints-discover.selectors.ts new file mode 100644 index 000000000..807c43ca6 --- /dev/null +++ b/src/app/features/preprints/store/preprints-discover/preprints-discover.selectors.ts @@ -0,0 +1,43 @@ +import { Selector } from '@ngxs/store'; + +import { Resource } from '@shared/models'; + +import { PreprintsDiscoverStateModel } from './preprints-discover.model'; +import { PreprintsDiscoverState } from './preprints-discover.state'; + +export class PreprintsDiscoverSelectors { + @Selector([PreprintsDiscoverState]) + static getResources(state: PreprintsDiscoverStateModel): Resource[] { + return state.resources.data; + } + + @Selector([PreprintsDiscoverState]) + static getResourcesCount(state: PreprintsDiscoverStateModel): number { + return state.resourcesCount; + } + + @Selector([PreprintsDiscoverState]) + static getSearchText(state: PreprintsDiscoverStateModel): string { + return state.searchText; + } + + @Selector([PreprintsDiscoverState]) + static getSortBy(state: PreprintsDiscoverStateModel): string { + return state.sortBy; + } + + @Selector([PreprintsDiscoverState]) + static getFirst(state: PreprintsDiscoverStateModel): string { + return state.first; + } + + @Selector([PreprintsDiscoverState]) + static getNext(state: PreprintsDiscoverStateModel): string { + return state.next; + } + + @Selector([PreprintsDiscoverState]) + static getPrevious(state: PreprintsDiscoverStateModel): string { + return state.previous; + } +} diff --git a/src/app/features/preprints/store/preprints-search/preprints-search.state.ts b/src/app/features/preprints/store/preprints-discover/preprints-discover.state.ts similarity index 67% rename from src/app/features/preprints/store/preprints-search/preprints-search.state.ts rename to src/app/features/preprints/store/preprints-discover/preprints-discover.state.ts index 6dff68dbf..7013ae701 100644 --- a/src/app/features/preprints/store/preprints-search/preprints-search.state.ts +++ b/src/app/features/preprints/store/preprints-discover/preprints-discover.state.ts @@ -4,34 +4,44 @@ import { BehaviorSubject, EMPTY, switchMap, tap } from 'rxjs'; import { inject, Injectable } from '@angular/core'; -import { PreprintsResourcesFiltersSelectors } from '@osf/features/preprints/store/preprints-resources-filters'; import { GetResources, GetResourcesByLink, - PreprintsSearchSelectors, ResetState, SetSearchText, SetSortBy, -} from '@osf/features/preprints/store/preprints-search'; +} from '@osf/features/preprints/store/preprints-discover/preprints-discover.actions'; +import { PreprintsDiscoverStateModel } from '@osf/features/preprints/store/preprints-discover/preprints-discover.model'; +import { PreprintsDiscoverSelectors } from '@osf/features/preprints/store/preprints-discover/preprints-discover.selectors'; +import { PreprintsResourcesFiltersSelectors } from '@osf/features/preprints/store/preprints-resources-filters'; import { ResourceFiltersStateModel } from '@osf/features/search/components/resource-filters/store'; -import { SearchService } from '@osf/shared/services'; -import { addFiltersParams, getResourceTypes } from '@osf/shared/utils'; -import { searchStateDefaults } from '@shared/constants'; import { GetResourcesRequestTypeEnum, ResourceTab } from '@shared/enums'; +import { SearchService } from '@shared/services'; +import { addFiltersParams, getResourceTypes } from '@shared/utils'; -import { PreprintsSearchStateModel } from './preprints-search.model'; - -@Injectable() -@State({ - name: 'preprintsSearch', - defaults: searchStateDefaults, +@State({ + name: 'preprintsDiscover', + defaults: { + resources: { + data: [], + isLoading: false, + error: null, + }, + resourcesCount: 0, + searchText: '', + sortBy: '-relevance', + first: '', + next: '', + previous: '', + }, }) -export class PreprintsSearchState implements NgxsOnInit { +@Injectable() +export class PreprintsDiscoverState implements NgxsOnInit { searchService = inject(SearchService); store = inject(Store); loadRequests = new BehaviorSubject<{ type: GetResourcesRequestTypeEnum; link?: string } | null>(null); - ngxsOnInit(ctx: StateContext): void { + ngxsOnInit(ctx: StateContext): void { this.loadRequests .pipe( switchMap((query) => { @@ -41,8 +51,8 @@ export class PreprintsSearchState implements NgxsOnInit { if (query.type === GetResourcesRequestTypeEnum.GetResources) { const filters = this.store.selectSnapshot(PreprintsResourcesFiltersSelectors.getAllFilters); const filtersParams = addFiltersParams(filters as ResourceFiltersStateModel); - const searchText = this.store.selectSnapshot(PreprintsSearchSelectors.getSearchText); - const sortBy = this.store.selectSnapshot(PreprintsSearchSelectors.getSortBy); + const searchText = this.store.selectSnapshot(PreprintsDiscoverSelectors.getSearchText); + const sortBy = this.store.selectSnapshot(PreprintsDiscoverSelectors.getSortBy); const resourceTab = ResourceTab.Preprints; const resourceTypes = getResourceTypes(resourceTab); @@ -89,7 +99,7 @@ export class PreprintsSearchState implements NgxsOnInit { } @Action(GetResourcesByLink) - getResourcesByLink(ctx: StateContext, action: GetResourcesByLink) { + getResourcesByLink(ctx: StateContext, action: GetResourcesByLink) { this.loadRequests.next({ type: GetResourcesRequestTypeEnum.GetResourcesByLink, link: action.link, @@ -97,17 +107,29 @@ export class PreprintsSearchState implements NgxsOnInit { } @Action(SetSearchText) - setSearchText(ctx: StateContext, action: SetSearchText) { + setSearchText(ctx: StateContext, action: SetSearchText) { ctx.patchState({ searchText: action.searchText }); } @Action(SetSortBy) - setSortBy(ctx: StateContext, action: SetSortBy) { + setSortBy(ctx: StateContext, action: SetSortBy) { ctx.patchState({ sortBy: action.sortBy }); } @Action(ResetState) - resetState(ctx: StateContext) { - ctx.patchState(searchStateDefaults); + resetState(ctx: StateContext) { + ctx.patchState({ + resources: { + data: [], + isLoading: false, + error: null, + }, + resourcesCount: 0, + searchText: '', + sortBy: '-relevance', + first: '', + next: '', + previous: '', + }); } } diff --git a/src/app/features/preprints/store/preprints-search/index.ts b/src/app/features/preprints/store/preprints-search/index.ts deleted file mode 100644 index 777222be7..000000000 --- a/src/app/features/preprints/store/preprints-search/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './preprints-search.actions'; -export * from './preprints-search.model'; -export * from './preprints-search.selectors'; -export * from './preprints-search.state'; diff --git a/src/app/features/preprints/store/preprints-search/preprints-search.actions.ts b/src/app/features/preprints/store/preprints-search/preprints-search.actions.ts deleted file mode 100644 index d6096ee82..000000000 --- a/src/app/features/preprints/store/preprints-search/preprints-search.actions.ts +++ /dev/null @@ -1,29 +0,0 @@ -export class GetResources { - static readonly type = '[Preprints search] Get Resources'; -} - -export class GetResourcesByLink { - static readonly type = '[Preprints search] Get Resources By Link'; - - constructor(public link: string) {} -} - -export class GetResourcesCount { - static readonly type = '[Preprints search] Get Resources Count'; -} - -export class SetSearchText { - static readonly type = '[Preprints search] Set Search Text'; - - constructor(public searchText: string) {} -} - -export class SetSortBy { - static readonly type = '[Preprints search] Set SortBy'; - - constructor(public sortBy: string) {} -} - -export class ResetState { - static readonly type = '[Preprints search] Reset State'; -} diff --git a/src/app/features/preprints/store/preprints-search/preprints-search.selectors.ts b/src/app/features/preprints/store/preprints-search/preprints-search.selectors.ts deleted file mode 100644 index 774d827ee..000000000 --- a/src/app/features/preprints/store/preprints-search/preprints-search.selectors.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Selector } from '@ngxs/store'; - -import { Resource } from '@osf/shared/models/resource-card/resource.model'; - -import { PreprintsSearchState, PreprintsSearchStateModel } from './'; - -export class PreprintsSearchSelectors { - @Selector([PreprintsSearchState]) - static getResources(state: PreprintsSearchStateModel): Resource[] { - return state.resources.data; - } - - @Selector([PreprintsSearchState]) - static getResourcesCount(state: PreprintsSearchStateModel): number { - return state.resourcesCount; - } - - @Selector([PreprintsSearchState]) - static getSearchText(state: PreprintsSearchStateModel): string { - return state.searchText; - } - - @Selector([PreprintsSearchState]) - static getSortBy(state: PreprintsSearchStateModel): string { - return state.sortBy; - } - - @Selector([PreprintsSearchState]) - static getFirst(state: PreprintsSearchStateModel): string { - return state.first; - } - - @Selector([PreprintsSearchState]) - static getNext(state: PreprintsSearchStateModel): string { - return state.next; - } - - @Selector([PreprintsSearchState]) - static getPrevious(state: PreprintsSearchStateModel): string { - return state.previous; - } -} From 870f229d1f3319f47c0eb0548947b154e3355def Mon Sep 17 00:00:00 2001 From: Roma Date: Wed, 11 Jun 2025 13:29:51 +0300 Subject: [PATCH 15/34] fix(state-provider): Providing states on route level --- src/app/core/constants/ngxs-states.constant.ts | 8 -------- .../preprints/constants/preprints.routes.ts | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/app/core/constants/ngxs-states.constant.ts b/src/app/core/constants/ngxs-states.constant.ts index ac13acd7e..7014a5980 100644 --- a/src/app/core/constants/ngxs-states.constant.ts +++ b/src/app/core/constants/ngxs-states.constant.ts @@ -4,10 +4,6 @@ import { CollectionsState } from '@osf/features/collections/store'; import { InstitutionsState } from '@osf/features/institutions/store'; import { MeetingsState } from '@osf/features/meetings/store'; import { MyProjectsState } from '@osf/features/my-projects/store'; -import { PreprintsState } from '@osf/features/preprints/store/preprints'; -import { PreprintsResourcesFiltersState } from '@osf/features/preprints/store/preprints-resources-filters'; -import { PreprintsResourcesFiltersOptionsState } from '@osf/features/preprints/store/preprints-resources-filters-options'; -import { PreprintsSearchState } from '@osf/features/preprints/store/preprints-search'; import { AnalyticsState } from '@osf/features/project/analytics/store'; import { ProjectOverviewState } from '@osf/features/project/overview/store'; import { RegistrationsState } from '@osf/features/project/registrations/store'; @@ -38,8 +34,4 @@ export const STATES = [ WikiState, MeetingsState, RegistrationsState, - PreprintsState, - PreprintsSearchState, - PreprintsResourcesFiltersState, - PreprintsResourcesFiltersOptionsState, ]; diff --git a/src/app/features/preprints/constants/preprints.routes.ts b/src/app/features/preprints/constants/preprints.routes.ts index c21fdbd4b..bdca37437 100644 --- a/src/app/features/preprints/constants/preprints.routes.ts +++ b/src/app/features/preprints/constants/preprints.routes.ts @@ -1,11 +1,25 @@ +import { provideStates } from '@ngxs/store'; + import { Routes } from '@angular/router'; import { PreprintsComponent } from '@osf/features/preprints/preprints.component'; +import { PreprintsState } from '@osf/features/preprints/store/preprints'; +import { PreprintsDiscoverState } from '@osf/features/preprints/store/preprints-discover'; +import { PreprintsResourcesFiltersState } from '@osf/features/preprints/store/preprints-resources-filters'; +import { PreprintsResourcesFiltersOptionsState } from '@osf/features/preprints/store/preprints-resources-filters-options'; export const preprintsRoutes: Routes = [ { path: '', component: PreprintsComponent, + providers: [ + provideStates([ + PreprintsState, + PreprintsDiscoverState, + PreprintsResourcesFiltersState, + PreprintsResourcesFiltersOptionsState, + ]), + ], children: [ { path: '', From 5df719979736a3c0db25fed8431bd589edff70cf Mon Sep 17 00:00:00 2001 From: Roma Date: Wed, 11 Jun 2025 14:56:49 +0300 Subject: [PATCH 16/34] refactor(sort-options): extracted to shared constants --- .../my-profile-resources.component.html | 6 +++--- .../my-profile-resources.component.ts | 17 +++++++---------- .../resources/resources.component.html | 6 +++--- .../components/resources/resources.component.ts | 13 +++---------- src/app/shared/constants/index.ts | 1 + .../constants/search-sort-options.const.ts | 7 +++++++ 6 files changed, 24 insertions(+), 26 deletions(-) create mode 100644 src/app/shared/constants/search-sort-options.const.ts diff --git a/src/app/features/my-profile/components/my-profile-resources/my-profile-resources.component.html b/src/app/features/my-profile/components/my-profile-resources/my-profile-resources.component.html index 6178a6a94..8085b18de 100644 --- a/src/app/features/my-profile/components/my-profile-resources/my-profile-resources.component.html +++ b/src/app/features/my-profile/components/my-profile-resources/my-profile-resources.component.html @@ -1,7 +1,7 @@
@if (isMobile()) { - + } @if (searchCount() > 10000) {

10 000+ results

@@ -16,7 +16,7 @@

0 results

@if (isWeb()) {

Sort by:

Sort by:
} @else if (isSortingOpen()) {
- @for (option of sortTabOptions; track option.value) { + @for (option of searchSortingOptions; track option.value) {
@if (isMobile()) { - + } @if (searchCount() > 10000) {

10 000+ results

@@ -16,7 +16,7 @@

0 results

@if (isWeb()) {

Sort by:

Sort by:
} @else if (isSortingOpen()) {
- @for (option of sortTabOptions; track option.value) { + @for (option of searchSortingOptions; track option.value) {
Date: Wed, 11 Jun 2025 14:58:09 +0300 Subject: [PATCH 17/34] feat(store): Added selectors to simplify logic --- .../preprints-resources-filters-options.selectors.ts | 10 ++++++++++ .../preprints-resources-filters.selectors.ts | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.selectors.ts b/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.selectors.ts index 6f24d9fb6..a75c7ed05 100644 --- a/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.selectors.ts +++ b/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.selectors.ts @@ -6,6 +6,16 @@ import { PreprintsResourceFiltersOptionsStateModel } from './preprints-resources import { PreprintsResourcesFiltersOptionsState } from './preprints-resources-filters-options.state'; export class PreprintsResourcesFiltersOptionsSelectors { + @Selector([PreprintsResourcesFiltersOptionsState]) + static isAnyFilterOptions(state: PreprintsResourceFiltersOptionsStateModel): boolean { + return ( + state.datesCreated.length > 0 || + state.subjects.length > 0 || + state.licenses.length > 0 || + state.providers.length > 0 + ); + } + @Selector([PreprintsResourcesFiltersOptionsState]) static getCreators(state: PreprintsResourceFiltersOptionsStateModel): Creator[] { return state.creators; diff --git a/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.selectors.ts b/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.selectors.ts index 2dec1f355..7a2a7f916 100644 --- a/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.selectors.ts +++ b/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.selectors.ts @@ -13,6 +13,11 @@ export class PreprintsResourcesFiltersSelectors { }; } + @Selector([PreprintsResourcesFiltersState]) + static isAnyFilterSelected(state: PreprintsResourcesFiltersStateModel): boolean { + return Boolean(state.dateCreated.value || state.subject.value || state.license.value || state.provider.value); + } + @Selector([PreprintsResourcesFiltersState]) static getCreator(state: PreprintsResourcesFiltersStateModel): ResourceFilterLabel { return state.creator; From d9bcb4a8c9a826061257900d256fdcdce193a873 Mon Sep 17 00:00:00 2001 From: Roma Date: Wed, 11 Jun 2025 15:40:53 +0300 Subject: [PATCH 18/34] refactor(resource-card-service): Moved to shared --- .../features/my-profile/components/index.ts | 1 - .../my-profile-resource-card.component.html | 182 ------------------ .../my-profile-resource-card.component.scss | 1 - ...my-profile-resource-card.component.spec.ts | 22 --- .../my-profile-resource-card.component.ts | 75 -------- .../my-profile-resources.component.html | 6 +- .../my-profile-resources.component.ts | 9 +- src/app/features/my-profile/services/index.ts | 1 - .../my-profile-resource-card.service.ts | 26 --- src/app/features/search/components/index.ts | 1 - .../resource-card.component.html | 181 ----------------- .../resource-card.component.scss | 1 - .../resource-card/resource-card.component.ts | 61 ------ .../resources/resources.component.html | 6 +- .../resources/resources.component.spec.ts | 2 +- .../resources/resources.component.ts | 5 +- src/app/features/search/services/index.ts | 1 - src/app/shared/components/index.ts | 1 + .../resource-card.component.html | 182 ++++++++++++++++++ .../resource-card.component.scss | 1 + .../resource-card.component.spec.ts | 8 +- .../resource-card/resource-card.component.ts | 72 +++++++ src/app/shared/services/index.ts | 1 + .../services/resource-card.service.ts | 12 +- 24 files changed, 279 insertions(+), 579 deletions(-) delete mode 100644 src/app/features/my-profile/components/my-profile-resource-card/my-profile-resource-card.component.html delete mode 100644 src/app/features/my-profile/components/my-profile-resource-card/my-profile-resource-card.component.scss delete mode 100644 src/app/features/my-profile/components/my-profile-resource-card/my-profile-resource-card.component.spec.ts delete mode 100644 src/app/features/my-profile/components/my-profile-resource-card/my-profile-resource-card.component.ts delete mode 100644 src/app/features/my-profile/services/my-profile-resource-card.service.ts delete mode 100644 src/app/features/search/components/resource-card/resource-card.component.html delete mode 100644 src/app/features/search/components/resource-card/resource-card.component.scss delete mode 100644 src/app/features/search/components/resource-card/resource-card.component.ts create mode 100644 src/app/shared/components/resource-card/resource-card.component.html create mode 100644 src/app/shared/components/resource-card/resource-card.component.scss rename src/app/{features/search => shared}/components/resource-card/resource-card.component.spec.ts (85%) create mode 100644 src/app/shared/components/resource-card/resource-card.component.ts rename src/app/{features/search => shared}/services/resource-card.service.ts (66%) diff --git a/src/app/features/my-profile/components/index.ts b/src/app/features/my-profile/components/index.ts index ebcca0578..45ced79dc 100644 --- a/src/app/features/my-profile/components/index.ts +++ b/src/app/features/my-profile/components/index.ts @@ -1,6 +1,5 @@ export * from './filters'; export { MyProfileFilterChipsComponent } from './my-profile-filter-chips/my-profile-filter-chips.component'; -export { MyProfileResourceCardComponent } from './my-profile-resource-card/my-profile-resource-card.component'; export { MyProfileResourceFiltersComponent } from './my-profile-resource-filters/my-profile-resource-filters.component'; export { MyProfileResourcesComponent } from './my-profile-resources/my-profile-resources.component'; export { MyProfileSearchComponent } from './my-profile-search/my-profile-search.component'; diff --git a/src/app/features/my-profile/components/my-profile-resource-card/my-profile-resource-card.component.html b/src/app/features/my-profile/components/my-profile-resource-card/my-profile-resource-card.component.html deleted file mode 100644 index 9d0040f19..000000000 --- a/src/app/features/my-profile/components/my-profile-resource-card/my-profile-resource-card.component.html +++ /dev/null @@ -1,182 +0,0 @@ -
- - - -
- @if (item()?.resourceType && item()?.resourceType === ResourceType.Agent) { -

User

- } @else if (item()?.resourceType) { -

{{ ResourceType[item()?.resourceType!] }}

- } - -
- @if (item()?.resourceType === ResourceType.File && item()?.fileName) { - {{ item()?.fileName }} - } @else if (item()?.title && item()?.title) { - @let url = item()?.id ? '/my-projects/' + item()?.id?.split('/')?.pop() + '/overview' : ''; - {{ item()?.title }} - } - @if (item()?.orcid) { - - orcid - - } -
- - @if (item()?.creators?.length) { -
- @for (creator of item()?.creators!.slice(0, 4); track creator.id; let i = $index) { - {{ creator.name }} - @if (i < (item()?.creators)!.length - 1 && i < 3) { - , - } - } - @if ((item()?.creators)!.length > 4) { -

 and {{ (item()?.creators)!.length - 4 }} more

- } -
- } - - @if (item()?.from?.id && item()?.from?.name) { - - } - - @if (item()?.dateCreated && item()?.dateModified) { -

- @if (!isSmall()) { - Date created: {{ item()?.dateCreated | date: 'MMMM d, y' }} | Date modified: - {{ item()?.dateModified | date: 'MMMM d, y' }} - } @else { -

-

Date created: {{ item()?.dateCreated | date: 'MMMM d, y' }}

-

- Date modified: - {{ item()?.dateModified | date: 'MMMM d, y' }} -

-
- } -

- } - - @if (item()?.resourceType === ResourceType.Registration) { - - } -
-
- -
- @if (item()?.description) { -

Description: {{ item()?.description }}

- } - - @if (item()?.provider?.id) { - -

Registration provider: 

- {{ item()?.provider?.name }} -
- } - - @if (item()?.license?.id) { - -

License: 

- {{ item()?.license?.name }} -
- } - - @if (item()?.registrationTemplate) { -

Registration Template: {{ item()?.registrationTemplate }}

- } - - @if (item()?.provider?.id) { - -

Provider: 

- {{ item()?.provider?.name }} -
- } - - @if (item()?.conflictOfInterestResponse && item()?.conflictOfInterestResponse === 'no-conflict-of-interest') { -

Conflict of Interest response: Author asserted no Conflict of Interest

- } - - @if (item()?.resourceType !== ResourceType.Agent && item()?.id) { - -

URL: 

- {{ item()?.id }} -
- } - - @if (item()?.doi) { - -

DOI: 

- {{ item()?.doi }} -
- } - - @if (item()?.resourceType === ResourceType.Agent) { - @if (loading) { - - - - } @else { -

Public projects: {{ item()?.publicProjects ?? 0 }}

-

Public registrations: {{ item()?.publicRegistrations ?? 0 }}

-

Public preprints: {{ item()?.publicPreprints ?? 0 }}

- } - } - - @if (item()?.employment) { -

Employment: {{ item()?.employment }}

- } - - @if (item()?.education) { -

Education: {{ item()?.education }}

- } -
-
-
-
-
diff --git a/src/app/features/my-profile/components/my-profile-resource-card/my-profile-resource-card.component.scss b/src/app/features/my-profile/components/my-profile-resource-card/my-profile-resource-card.component.scss deleted file mode 100644 index f65fb1607..000000000 --- a/src/app/features/my-profile/components/my-profile-resource-card/my-profile-resource-card.component.scss +++ /dev/null @@ -1 +0,0 @@ -@use "assets/styles/components/resource-card" as resource-card; diff --git a/src/app/features/my-profile/components/my-profile-resource-card/my-profile-resource-card.component.spec.ts b/src/app/features/my-profile/components/my-profile-resource-card/my-profile-resource-card.component.spec.ts deleted file mode 100644 index 58d77776e..000000000 --- a/src/app/features/my-profile/components/my-profile-resource-card/my-profile-resource-card.component.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { MyProfileResourceCardComponent } from './my-profile-resource-card.component'; - -describe('MyProfileResourceCardComponent', () => { - let component: MyProfileResourceCardComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [MyProfileResourceCardComponent], - }).compileComponents(); - - fixture = TestBed.createComponent(MyProfileResourceCardComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/features/my-profile/components/my-profile-resource-card/my-profile-resource-card.component.ts b/src/app/features/my-profile/components/my-profile-resource-card/my-profile-resource-card.component.ts deleted file mode 100644 index 73747da2a..000000000 --- a/src/app/features/my-profile/components/my-profile-resource-card/my-profile-resource-card.component.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { Accordion, AccordionContent, AccordionHeader, AccordionPanel } from 'primeng/accordion'; -import { Skeleton } from 'primeng/skeleton'; - -import { finalize } from 'rxjs'; - -import { DatePipe, NgOptimizedImage } from '@angular/common'; -import { ChangeDetectionStrategy, Component, inject, model } from '@angular/core'; -import { toSignal } from '@angular/core/rxjs-interop'; -import { RouterLink } from '@angular/router'; - -import { ResourceType } from '@osf/shared/enums'; -import { Resource } from '@osf/shared/models'; -import { IS_XSMALL } from '@osf/shared/utils'; - -import { ResourceCardService } from '../../services'; - -import { environment } from 'src/environments/environment'; - -@Component({ - selector: 'osf-my-profile-resource-card', - imports: [ - Accordion, - AccordionContent, - AccordionHeader, - AccordionPanel, - DatePipe, - NgOptimizedImage, - Skeleton, - RouterLink, - ], - templateUrl: './my-profile-resource-card.component.html', - styleUrl: './my-profile-resource-card.component.scss', - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class MyProfileResourceCardComponent { - item = model(undefined); - readonly #resourceCardService = inject(ResourceCardService); - loading = false; - dataIsLoaded = false; - isSmall = toSignal(inject(IS_XSMALL)); - - protected readonly ResourceType = ResourceType; - - onOpen() { - if (this.item() && !this.dataIsLoaded) { - const userIri = this.item()?.id.split('/').pop(); - if (userIri) { - this.loading = true; - this.#resourceCardService - .getUserRelatedCounts(userIri) - .pipe( - finalize(() => { - this.loading = false; - this.dataIsLoaded = true; - }) - ) - .subscribe((res) => { - this.item.update( - (current) => - ({ - ...current, - publicProjects: res.projects, - publicPreprints: res.preprints, - publicRegistrations: res.registrations, - education: res.education, - employment: res.employment, - }) as Resource - ); - }); - } - } - } - - protected readonly environment = environment; -} diff --git a/src/app/features/my-profile/components/my-profile-resources/my-profile-resources.component.html b/src/app/features/my-profile/components/my-profile-resources/my-profile-resources.component.html index 8085b18de..70aa971f4 100644 --- a/src/app/features/my-profile/components/my-profile-resources/my-profile-resources.component.html +++ b/src/app/features/my-profile/components/my-profile-resources/my-profile-resources.component.html @@ -80,12 +80,12 @@

Sort by:

} - +
@if (items.length > 0) { @for (item of items; track item.id) { - + }
@@ -144,6 +144,6 @@

Sort by:

}
- +
} diff --git a/src/app/features/my-profile/components/my-profile-resources/my-profile-resources.component.ts b/src/app/features/my-profile/components/my-profile-resources/my-profile-resources.component.ts index 5e396b787..1ac8619b9 100644 --- a/src/app/features/my-profile/components/my-profile-resources/my-profile-resources.component.ts +++ b/src/app/features/my-profile/components/my-profile-resources/my-profile-resources.component.ts @@ -8,13 +8,10 @@ import { ChangeDetectionStrategy, Component, computed, effect, inject, signal, u import { toSignal } from '@angular/core/rxjs-interop'; import { FormsModule } from '@angular/forms'; -import { - MyProfileFilterChipsComponent, - MyProfileResourceCardComponent, - MyProfileResourceFiltersComponent, -} from '@osf/features/my-profile/components'; +import { MyProfileFilterChipsComponent, MyProfileResourceFiltersComponent } from '@osf/features/my-profile/components'; import { ResourceTab } from '@osf/shared/enums'; import { IS_WEB, IS_XSMALL } from '@osf/shared/utils'; +import { ResourceCardComponent } from '@shared/components/resource-card/resource-card.component'; import { searchSortingOptions } from '@shared/constants'; import { GetResourcesByLink, MyProfileSelectors, SetResourceTab, SetSortBy } from '../../store'; @@ -27,10 +24,10 @@ import { MyProfileResourceFiltersSelectors } from '../my-profile-resource-filter DataView, MyProfileFilterChipsComponent, NgOptimizedImage, - MyProfileResourceCardComponent, MyProfileResourceFiltersComponent, Select, FormsModule, + ResourceCardComponent, ], templateUrl: './my-profile-resources.component.html', styleUrl: './my-profile-resources.component.scss', diff --git a/src/app/features/my-profile/services/index.ts b/src/app/features/my-profile/services/index.ts index a5c40bd4c..4eb8401b2 100644 --- a/src/app/features/my-profile/services/index.ts +++ b/src/app/features/my-profile/services/index.ts @@ -1,2 +1 @@ -export { ResourceCardService } from './my-profile-resource-card.service'; export { MyProfileFiltersOptionsService } from './my-profile-resource-filters.service'; diff --git a/src/app/features/my-profile/services/my-profile-resource-card.service.ts b/src/app/features/my-profile/services/my-profile-resource-card.service.ts deleted file mode 100644 index f1dc221a0..000000000 --- a/src/app/features/my-profile/services/my-profile-resource-card.service.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { map, Observable } from 'rxjs'; - -import { inject, Injectable } from '@angular/core'; - -import { JsonApiService } from '@osf/core/services'; -import { MapUserCounts } from '@osf/shared/mappers'; -import { UserCountsResponse, UserRelatedDataCounts } from '@osf/shared/models'; - -import { environment } from 'src/environments/environment'; - -@Injectable({ - providedIn: 'root', -}) -export class ResourceCardService { - #jsonApiService = inject(JsonApiService); - - getUserRelatedCounts(userIri: string): Observable { - const params: Record = { - related_counts: 'nodes,registrations,preprints', - }; - - return this.#jsonApiService - .get(`${environment.apiUrl}/users/${userIri}`, params) - .pipe(map((response) => MapUserCounts(response))); - } -} diff --git a/src/app/features/search/components/index.ts b/src/app/features/search/components/index.ts index b80a5b0d5..fa4051313 100644 --- a/src/app/features/search/components/index.ts +++ b/src/app/features/search/components/index.ts @@ -1,6 +1,5 @@ export { FilterChipsComponent } from './filter-chips/filter-chips.component'; export * from './filters'; -export { ResourceCardComponent } from './resource-card/resource-card.component'; export { ResourceFiltersComponent } from './resource-filters/resource-filters.component'; export { ResourcesComponent } from './resources/resources.component'; export { ResourcesWrapperComponent } from './resources-wrapper/resources-wrapper.component'; diff --git a/src/app/features/search/components/resource-card/resource-card.component.html b/src/app/features/search/components/resource-card/resource-card.component.html deleted file mode 100644 index 74bfd69c9..000000000 --- a/src/app/features/search/components/resource-card/resource-card.component.html +++ /dev/null @@ -1,181 +0,0 @@ -
- - - -
- @if (item()?.resourceType && item()?.resourceType === ResourceType.Agent) { -

User

- } @else if (item()?.resourceType) { -

{{ ResourceType[item()?.resourceType!] }}

- } - -
- @if (item()?.resourceType === ResourceType.File && item()?.fileName) { - {{ item()?.fileName }} - } @else if (item()?.title && item()?.title) { - {{ item()?.title }} - } - @if (item()?.orcid) { - - orcid - - } -
- - @if (item()?.creators?.length) { -
- @for (creator of item()?.creators!.slice(0, 4); track creator.id; let i = $index) { - {{ creator.name }} - @if (i < (item()?.creators)!.length - 1 && i < 3) { - , - } - } - @if ((item()?.creators)!.length > 4) { -

 and {{ (item()?.creators)!.length - 4 }} more

- } -
- } - - @if (item()?.from?.id && item()?.from?.name) { - - } - - @if (item()?.dateCreated && item()?.dateModified) { -

- @if (!isSmall()) { - Date created: {{ item()?.dateCreated | date: 'MMMM d, y' }} | Date modified: - {{ item()?.dateModified | date: 'MMMM d, y' }} - } @else { -

-

Date created: {{ item()?.dateCreated | date: 'MMMM d, y' }}

-

- Date modified: - {{ item()?.dateModified | date: 'MMMM d, y' }} -

-
- } -

- } - - @if (item()?.resourceType === ResourceType.Registration) { - - } -
-
- -
- @if (item()?.description) { -

Description: {{ item()?.description }}

- } - - @if (item()?.provider?.id) { - -

Registration provider: 

- {{ item()?.provider?.name }} -
- } - - @if (item()?.license?.id) { - -

License: 

- {{ item()?.license?.name }} -
- } - - @if (item()?.registrationTemplate) { -

Registration Template: {{ item()?.registrationTemplate }}

- } - - @if (item()?.provider?.id) { - -

Provider: 

- {{ item()?.provider?.name }} -
- } - - @if (item()?.conflictOfInterestResponse && item()?.conflictOfInterestResponse === 'no-conflict-of-interest') { -

Conflict of Interest response: Author asserted no Conflict of Interest

- } - - @if (item()?.resourceType !== ResourceType.Agent && item()?.id) { - -

URL: 

- {{ item()?.id }} -
- } - - @if (item()?.doi) { - -

DOI: 

- {{ item()?.doi }} -
- } - - @if (item()?.resourceType === ResourceType.Agent) { - @if (loading) { - - - - } @else { -

Public projects: {{ item()?.publicProjects ?? 0 }}

-

Public registrations: {{ item()?.publicRegistrations ?? 0 }}

-

Public preprints: {{ item()?.publicPreprints ?? 0 }}

- } - } - - @if (item()?.employment) { -

Employment: {{ item()?.employment }}

- } - - @if (item()?.education) { -

Education: {{ item()?.education }}

- } -
-
-
-
-
diff --git a/src/app/features/search/components/resource-card/resource-card.component.scss b/src/app/features/search/components/resource-card/resource-card.component.scss deleted file mode 100644 index f65fb1607..000000000 --- a/src/app/features/search/components/resource-card/resource-card.component.scss +++ /dev/null @@ -1 +0,0 @@ -@use "assets/styles/components/resource-card" as resource-card; diff --git a/src/app/features/search/components/resource-card/resource-card.component.ts b/src/app/features/search/components/resource-card/resource-card.component.ts deleted file mode 100644 index e0773cac0..000000000 --- a/src/app/features/search/components/resource-card/resource-card.component.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Accordion, AccordionContent, AccordionHeader, AccordionPanel } from 'primeng/accordion'; -import { Skeleton } from 'primeng/skeleton'; - -import { finalize } from 'rxjs'; - -import { DatePipe, NgOptimizedImage } from '@angular/common'; -import { ChangeDetectionStrategy, Component, inject, model } from '@angular/core'; -import { toSignal } from '@angular/core/rxjs-interop'; - -import { ResourceType } from '@osf/shared/enums'; -import { Resource } from '@osf/shared/models'; -import { IS_XSMALL } from '@osf/shared/utils'; - -import { ResourceCardService } from '../../services'; - -@Component({ - selector: 'osf-resource-card', - imports: [Accordion, AccordionContent, AccordionHeader, AccordionPanel, DatePipe, NgOptimizedImage, Skeleton], - templateUrl: './resource-card.component.html', - styleUrl: './resource-card.component.scss', - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class ResourceCardComponent { - item = model(undefined); - readonly #resourceCardService = inject(ResourceCardService); - loading = false; - dataIsLoaded = false; - isSmall = toSignal(inject(IS_XSMALL)); - - protected readonly ResourceType = ResourceType; - - onOpen() { - if (this.item() && !this.dataIsLoaded) { - const userIri = this.item()?.id.split('/').pop(); - if (userIri) { - this.loading = true; - this.#resourceCardService - .getUserRelatedCounts(userIri) - .pipe( - finalize(() => { - this.loading = false; - this.dataIsLoaded = true; - }) - ) - .subscribe((res) => { - this.item.update( - (current) => - ({ - ...current, - publicProjects: res.projects, - publicPreprints: res.preprints, - publicRegistrations: res.registrations, - education: res.education, - employment: res.employment, - }) as Resource - ); - }); - } - } - } -} diff --git a/src/app/features/search/components/resources/resources.component.html b/src/app/features/search/components/resources/resources.component.html index de9552c05..9a28361e3 100644 --- a/src/app/features/search/components/resources/resources.component.html +++ b/src/app/features/search/components/resources/resources.component.html @@ -81,12 +81,12 @@

Sort by:

} - +
@if (items.length > 0) { @for (item of items; track item.id) { - + }
@@ -145,6 +145,6 @@

Sort by:

}
- +
} diff --git a/src/app/features/search/components/resources/resources.component.spec.ts b/src/app/features/search/components/resources/resources.component.spec.ts index 4283f6ec1..4692ddf54 100644 --- a/src/app/features/search/components/resources/resources.component.spec.ts +++ b/src/app/features/search/components/resources/resources.component.spec.ts @@ -8,11 +8,11 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ResourceTab } from '@osf/shared/enums'; import { IS_WEB, IS_XSMALL } from '@osf/shared/utils'; +import { ResourceCardComponent } from '@shared/components/resource-card/resource-card.component'; import { GetResourcesByLink, SearchSelectors } from '../../store'; import { FilterChipsComponent } from '../filter-chips/filter-chips.component'; import { ResourceFiltersOptionsSelectors } from '../filters/store'; -import { ResourceCardComponent } from '../resource-card/resource-card.component'; import { ResourceFiltersComponent } from '../resource-filters/resource-filters.component'; import { ResourceFiltersSelectors } from '../resource-filters/store'; diff --git a/src/app/features/search/components/resources/resources.component.ts b/src/app/features/search/components/resources/resources.component.ts index 2ec19f8cb..a5f310593 100644 --- a/src/app/features/search/components/resources/resources.component.ts +++ b/src/app/features/search/components/resources/resources.component.ts @@ -11,9 +11,10 @@ import { ChangeDetectionStrategy, Component, computed, effect, inject, signal, u import { toSignal } from '@angular/core/rxjs-interop'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { FilterChipsComponent, ResourceCardComponent, ResourceFiltersComponent } from '@osf/features/search/components'; +import { FilterChipsComponent, ResourceFiltersComponent } from '@osf/features/search/components'; import { ResourceTab } from '@osf/shared/enums'; import { IS_WEB, IS_XSMALL } from '@osf/shared/utils'; +import { ResourceCardComponent } from '@shared/components'; import { searchSortingOptions } from '@shared/constants'; import { GetResourcesByLink, SearchSelectors, SetResourceTab, SetSortBy } from '../../store'; @@ -30,10 +31,10 @@ import { ResourceFiltersSelectors } from '../resource-filters/store'; AccordionModule, TableModule, DataViewModule, - ResourceCardComponent, FilterChipsComponent, Select, NgOptimizedImage, + ResourceCardComponent, ], templateUrl: './resources.component.html', styleUrl: './resources.component.scss', diff --git a/src/app/features/search/services/index.ts b/src/app/features/search/services/index.ts index 70f3be8ac..29ca64498 100644 --- a/src/app/features/search/services/index.ts +++ b/src/app/features/search/services/index.ts @@ -1,2 +1 @@ -export { ResourceCardService } from './resource-card.service'; export { ResourceFiltersService } from './resource-filters.service'; diff --git a/src/app/shared/components/index.ts b/src/app/shared/components/index.ts index 9b3862021..d37535e65 100644 --- a/src/app/shared/components/index.ts +++ b/src/app/shared/components/index.ts @@ -11,6 +11,7 @@ export { LoadingSpinnerComponent } from './loading-spinner/loading-spinner.compo export { MyProjectsTableComponent } from './my-projects-table/my-projects-table.component'; export { PasswordInputHintComponent } from './password-input-hint/password-input-hint.component'; export { PieChartComponent } from './pie-chart/pie-chart.component'; +export { ResourceCardComponent } from './resource-card/resource-card.component'; export { SearchInputComponent } from './search-input/search-input.component'; export { SubHeaderComponent } from './sub-header/sub-header.component'; export { TextInputComponent } from './text-input/text-input.component'; diff --git a/src/app/shared/components/resource-card/resource-card.component.html b/src/app/shared/components/resource-card/resource-card.component.html new file mode 100644 index 000000000..cd142160c --- /dev/null +++ b/src/app/shared/components/resource-card/resource-card.component.html @@ -0,0 +1,182 @@ +
+ + + +
+ @if (item().resourceType && item().resourceType === ResourceType.Agent) { +

User

+ } @else if (item().resourceType) { +

{{ ResourceType[item().resourceType!] }}

+ } + +
+ @if (item().resourceType === ResourceType.File && item().fileName) { + {{ item().fileName }} + } @else if (item().title && item().title) { + @let url = item().id ? '/my-projects/' + item().id.split('/').pop() + '/overview' : ''; + {{ item().title }} + } + @if (item().orcid) { + + orcid + + } +
+ + @if (item().creators?.length) { +
+ @for (creator of item().creators!.slice(0, 4); track creator.id; let i = $index) { + {{ creator.name }} + @if (i < item().creators!.length - 1 && i < 3) { + , + } + } + @if (item().creators!.length > 4) { +

 and {{ item().creators!.length - 4 }} more

+ } +
+ } + + @if (item().from?.id && item().from?.name) { + + } + + @if (item().dateCreated && item().dateModified) { +

+ @if (!isSmall()) { + Date created: {{ item().dateCreated | date: 'MMMM d, y' }} | Date modified: + {{ item().dateModified | date: 'MMMM d, y' }} + } @else { +

+

Date created: {{ item().dateCreated | date: 'MMMM d, y' }}

+

+ Date modified: + {{ item().dateModified | date: 'MMMM d, y' }} +

+
+ } +

+ } + + @if (item().resourceType === ResourceType.Registration) { + + } +
+
+ +
+ @if (item().description) { +

Description: {{ item().description }}

+ } + + @if (item().provider?.id) { + +

Registration provider: 

+ {{ item().provider?.name }} +
+ } + + @if (item().license?.id) { + +

License: 

+ {{ item().license?.name }} +
+ } + + @if (item().registrationTemplate) { +

Registration Template: {{ item().registrationTemplate }}

+ } + + @if (item().provider?.id) { + +

Provider: 

+ {{ item().provider?.name }} +
+ } + + @if (item().conflictOfInterestResponse && item().conflictOfInterestResponse === 'no-conflict-of-interest') { +

Conflict of Interest response: Author asserted no Conflict of Interest

+ } + + @if (item().resourceType !== ResourceType.Agent && item().id) { + +

URL:

+ {{ item().id }} +
+ } + + @if (item().doi) { + +

DOI: 

+ {{ item().doi }} +
+ } + + @if (item().resourceType === ResourceType.Agent) { + @if (isLoading) { + + + + } @else { +

Public projects: {{ item().publicProjects ?? 0 }}

+

Public registrations: {{ item().publicRegistrations ?? 0 }}

+

Public preprints: {{ item().publicPreprints ?? 0 }}

+ } + } + + @if (item().employment) { +

Employment: {{ item().employment }}

+ } + + @if (item().education) { +

Education: {{ item().education }}

+ } +
+
+
+
+
diff --git a/src/app/shared/components/resource-card/resource-card.component.scss b/src/app/shared/components/resource-card/resource-card.component.scss new file mode 100644 index 000000000..9c202cc5f --- /dev/null +++ b/src/app/shared/components/resource-card/resource-card.component.scss @@ -0,0 +1 @@ +@use "../../../../assets/styles/components/resource-card" as resource-card; diff --git a/src/app/features/search/components/resource-card/resource-card.component.spec.ts b/src/app/shared/components/resource-card/resource-card.component.spec.ts similarity index 85% rename from src/app/features/search/components/resource-card/resource-card.component.spec.ts rename to src/app/shared/components/resource-card/resource-card.component.spec.ts index 7aad6b7cc..1a1175a10 100644 --- a/src/app/features/search/components/resource-card/resource-card.component.spec.ts +++ b/src/app/shared/components/resource-card/resource-card.component.spec.ts @@ -4,11 +4,9 @@ import { of } from 'rxjs'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { IS_XSMALL } from '@osf/shared/utils'; - -import { ResourceCardService } from '../../services'; - -import { ResourceCardComponent } from './resource-card.component'; +import { ResourceCardComponent } from '@shared/components'; +import { ResourceCardService } from '@shared/services'; +import { IS_XSMALL } from '@shared/utils'; describe('MyProfileResourceCardComponent', () => { let component: ResourceCardComponent; diff --git a/src/app/shared/components/resource-card/resource-card.component.ts b/src/app/shared/components/resource-card/resource-card.component.ts new file mode 100644 index 000000000..d4b251cd7 --- /dev/null +++ b/src/app/shared/components/resource-card/resource-card.component.ts @@ -0,0 +1,72 @@ +import { Accordion, AccordionContent, AccordionHeader, AccordionPanel } from 'primeng/accordion'; +import { Skeleton } from 'primeng/skeleton'; + +import { finalize } from 'rxjs'; + +import { DatePipe, NgOptimizedImage } from '@angular/common'; +import { ChangeDetectionStrategy, Component, inject, model } from '@angular/core'; +import { toSignal } from '@angular/core/rxjs-interop'; +import { RouterLink } from '@angular/router'; + +import { ResourceType } from '@shared/enums'; +import { Resource } from '@shared/models'; +import { ResourceCardService } from '@shared/services'; +import { IS_XSMALL } from '@shared/utils'; + +@Component({ + selector: 'osf-resource-card', + imports: [ + Accordion, + AccordionContent, + AccordionHeader, + AccordionPanel, + DatePipe, + NgOptimizedImage, + Skeleton, + RouterLink, + ], + templateUrl: './resource-card.component.html', + styleUrl: './resource-card.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ResourceCardComponent { + private readonly resourceCardService = inject(ResourceCardService); + ResourceType = ResourceType; + isSmall = toSignal(inject(IS_XSMALL)); + item = model.required(); + + isLoading = false; + dataIsLoaded = false; + + onOpen() { + if (!this.item() || this.dataIsLoaded) { + return; + } + + const userIri = this.item()?.id.split('/').pop(); + if (userIri) { + this.isLoading = true; + this.resourceCardService + .getUserRelatedCounts(userIri) + .pipe( + finalize(() => { + this.isLoading = false; + this.dataIsLoaded = true; + }) + ) + .subscribe((res) => { + this.item.update( + (current) => + ({ + ...current, + publicProjects: res.projects, + publicPreprints: res.preprints, + publicRegistrations: res.registrations, + education: res.education, + employment: res.employment, + }) as Resource + ); + }); + } + } +} diff --git a/src/app/shared/services/index.ts b/src/app/shared/services/index.ts index e3aa084cc..63411591b 100644 --- a/src/app/shared/services/index.ts +++ b/src/app/shared/services/index.ts @@ -1,4 +1,5 @@ export { FiltersOptionsService } from './filters-options.service'; export { LoaderService } from './loader.service'; +export { ResourceCardService } from './resource-card.service'; export { SearchService } from './search.service'; export { ToastService } from './toast.service'; diff --git a/src/app/features/search/services/resource-card.service.ts b/src/app/shared/services/resource-card.service.ts similarity index 66% rename from src/app/features/search/services/resource-card.service.ts rename to src/app/shared/services/resource-card.service.ts index f1dc221a0..aeaf1eb49 100644 --- a/src/app/features/search/services/resource-card.service.ts +++ b/src/app/shared/services/resource-card.service.ts @@ -2,9 +2,9 @@ import { map, Observable } from 'rxjs'; import { inject, Injectable } from '@angular/core'; -import { JsonApiService } from '@osf/core/services'; -import { MapUserCounts } from '@osf/shared/mappers'; -import { UserCountsResponse, UserRelatedDataCounts } from '@osf/shared/models'; +import { JsonApiService } from '@core/services'; +import { MapUserCounts } from '@shared/mappers'; +import { UserCountsResponse, UserRelatedDataCounts } from '@shared/models'; import { environment } from 'src/environments/environment'; @@ -12,15 +12,15 @@ import { environment } from 'src/environments/environment'; providedIn: 'root', }) export class ResourceCardService { - #jsonApiService = inject(JsonApiService); + private jsonApiService = inject(JsonApiService); getUserRelatedCounts(userIri: string): Observable { const params: Record = { related_counts: 'nodes,registrations,preprints', }; - return this.#jsonApiService - .get(`${environment.apiUrl}/users/${userIri}`, params) + return this.jsonApiService + .get(`${environment.apiUrl}/users/${userIri}/`, params) .pipe(map((response) => MapUserCounts(response))); } } From 16a16d593a967cef8aad99a9da5a599ed4246f1b Mon Sep 17 00:00:00 2001 From: Roma Date: Wed, 11 Jun 2025 18:48:37 +0300 Subject: [PATCH 19/34] fix(preprints-filter-chips): Add PrimeTemplate import --- .../preprints-filter-chips.component.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.ts b/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.ts index 047a747de..8132ef171 100644 --- a/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.ts +++ b/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.ts @@ -1,25 +1,24 @@ import { createDispatchMap, select } from '@ngxs/store'; +import { PrimeTemplate } from 'primeng/api'; import { Chip } from 'primeng/chip'; import { ChangeDetectionStrategy, Component } from '@angular/core'; import { PreprintsResourcesFiltersSelectors, - SetProvider, -} from '@osf/features/preprints/store/preprints-resources-filters'; -import { GetAllOptions } from '@osf/features/preprints/store/preprints-resources-filters-options'; -import { SetCreator, SetDateCreated, SetLicense, + SetProvider, SetSubject, -} from '@osf/features/search/components/resource-filters/store'; +} from '@osf/features/preprints/store/preprints-resources-filters'; +import { GetAllOptions } from '@osf/features/preprints/store/preprints-resources-filters-options'; import { FilterType } from '@osf/shared/enums'; @Component({ selector: 'osf-preprints-filter-chips', - imports: [Chip], + imports: [Chip, PrimeTemplate], templateUrl: './preprints-filter-chips.component.html', styleUrl: './preprints-filter-chips.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, From 66643e744809cff15379809c8df2538d724220bc Mon Sep 17 00:00:00 2001 From: Roma Date: Wed, 11 Jun 2025 18:52:41 +0300 Subject: [PATCH 20/34] feat(preprints-resources): Implemented section to handle sorting, filters, search cards --- .../preprints-resources.component.html | 147 ++++++++++++++++++ .../preprints-resources.component.scss | 43 +++++ .../preprints-resources.component.spec.ts | 22 +++ .../preprints-resources.component.ts | 77 +++++++++ .../preprint-provider-discover.component.html | 2 + 5 files changed, 291 insertions(+) create mode 100644 src/app/features/preprints/components/preprints-resources/preprints-resources.component.html create mode 100644 src/app/features/preprints/components/preprints-resources/preprints-resources.component.scss create mode 100644 src/app/features/preprints/components/preprints-resources/preprints-resources.component.spec.ts create mode 100644 src/app/features/preprints/components/preprints-resources/preprints-resources.component.ts diff --git a/src/app/features/preprints/components/preprints-resources/preprints-resources.component.html b/src/app/features/preprints/components/preprints-resources/preprints-resources.component.html new file mode 100644 index 000000000..0a8815bb7 --- /dev/null +++ b/src/app/features/preprints/components/preprints-resources/preprints-resources.component.html @@ -0,0 +1,147 @@ +
+
+ @if (resourcesCount() > 10000) { +

10 000+ results

+ } @else if (resourcesCount() > 0) { +

{{ resourcesCount() }} results

+ } @else { +

0 results

+ } + +
+ @if (isWeb()) { +

Sort by:

+ + } @else { + @if (isAnyFilterOptions()) { + filter by + } + sort by + } +
+
+ + @if (isFiltersOpen()) { +
+ +
+ } @else if (isSortingOpen()) { +
+ @for (option of searchSortingOptions; track option.value) { +
+ {{ option.label }} +
+ } +
+ } @else { + @if (isAnyFilterSelected()) { +
+ +
+ } + +
+ @if (isWeb() && isAnyFilterOptions()) { + + } + + + +
+ @if (items.length > 0) { + @for (item of items; track item.id) { + + } + +
+ @if (first() && prev()) { + + + + } + + + + + + + + +
+ } +
+
+
+
+ } +
diff --git a/src/app/features/preprints/components/preprints-resources/preprints-resources.component.scss b/src/app/features/preprints/components/preprints-resources/preprints-resources.component.scss new file mode 100644 index 000000000..56362826c --- /dev/null +++ b/src/app/features/preprints/components/preprints-resources/preprints-resources.component.scss @@ -0,0 +1,43 @@ +@use "assets/styles/variables" as var; +@use "assets/styles/mixins" as mix; + +h4 { + color: var.$pr-blue-1; +} + +.sorting-container { + display: flex; + align-items: center; + gap: mix.rem(6px); + + h4 { + color: var.$dark-blue-1; + font-weight: 400; + text-wrap: nowrap; + } +} + +.sort-card { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: mix.rem(44px); + border: 1px solid var.$grey-2; + border-radius: mix.rem(12px); + padding: 0 mix.rem(24px) 0 mix.rem(24px); + cursor: pointer; +} + +.card-selected { + background: var.$bg-blue-2; +} + +.icon-disabled { + opacity: 0.5; + cursor: none; +} + +.icon-active { + fill: var.$grey-1; +} diff --git a/src/app/features/preprints/components/preprints-resources/preprints-resources.component.spec.ts b/src/app/features/preprints/components/preprints-resources/preprints-resources.component.spec.ts new file mode 100644 index 000000000..c4f65e4b8 --- /dev/null +++ b/src/app/features/preprints/components/preprints-resources/preprints-resources.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PreprintsResourcesComponent } from './preprints-resources.component'; + +describe('PreprintsResourcesComponent', () => { + let component: PreprintsResourcesComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PreprintsResourcesComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(PreprintsResourcesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/preprints/components/preprints-resources/preprints-resources.component.ts b/src/app/features/preprints/components/preprints-resources/preprints-resources.component.ts new file mode 100644 index 000000000..766f9fa98 --- /dev/null +++ b/src/app/features/preprints/components/preprints-resources/preprints-resources.component.ts @@ -0,0 +1,77 @@ +import { createDispatchMap, select } from '@ngxs/store'; + +import { DataView } from 'primeng/dataview'; +import { Select } from 'primeng/select'; + +import { NgOptimizedImage } from '@angular/common'; +import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core'; +import { toSignal } from '@angular/core/rxjs-interop'; +import { FormsModule } from '@angular/forms'; + +import { Primitive } from '@core/helpers'; +import { PreprintsFilterChipsComponent, PreprintsResourcesFiltersComponent } from '@osf/features/preprints/components'; +import { + GetResourcesByLink, + PreprintsDiscoverSelectors, + SetSortBy, +} from '@osf/features/preprints/store/preprints-discover'; +import { PreprintsResourcesFiltersSelectors } from '@osf/features/preprints/store/preprints-resources-filters'; +import { PreprintsResourcesFiltersOptionsSelectors } from '@osf/features/preprints/store/preprints-resources-filters-options'; +import { ResourceCardComponent } from '@shared/components'; +import { searchSortingOptions } from '@shared/constants'; +import { IS_WEB, IS_XSMALL } from '@shared/utils'; + +@Component({ + selector: 'osf-preprints-resources', + imports: [ + Select, + FormsModule, + NgOptimizedImage, + PreprintsResourcesFiltersComponent, + PreprintsFilterChipsComponent, + DataView, + ResourceCardComponent, + ], + templateUrl: './preprints-resources.component.html', + styleUrl: './preprints-resources.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class PreprintsResourcesComponent { + private readonly actions = createDispatchMap({ setSortBy: SetSortBy, getResourcesByLink: GetResourcesByLink }); + searchSortingOptions = searchSortingOptions; + + isWeb = toSignal(inject(IS_WEB)); + isMobile = toSignal(inject(IS_XSMALL)); + + resources = select(PreprintsDiscoverSelectors.getResources); + resourcesCount = select(PreprintsDiscoverSelectors.getResourcesCount); + + sortBy = select(PreprintsDiscoverSelectors.getSortBy); + first = select(PreprintsDiscoverSelectors.getFirst); + next = select(PreprintsDiscoverSelectors.getNext); + prev = select(PreprintsDiscoverSelectors.getPrevious); + + isSortingOpen = signal(false); + isFiltersOpen = signal(false); + + isAnyFilterSelected = select(PreprintsResourcesFiltersSelectors.getAllFilters); + isAnyFilterOptions = select(PreprintsResourcesFiltersOptionsSelectors.isAnyFilterOptions); + + switchPage(link: string) { + this.actions.getResourcesByLink(link); + } + + switchMobileFiltersSectionVisibility() { + this.isFiltersOpen.set(!this.isFiltersOpen()); + this.isSortingOpen.set(false); + } + + switchMobileSortingSectionVisibility() { + this.isSortingOpen.set(!this.isSortingOpen()); + this.isFiltersOpen.set(false); + } + + sortOptionSelected(value: Primitive) { + this.actions.setSortBy(value as string); + } +} diff --git a/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.html b/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.html index bfa76be2a..52fe2811e 100644 --- a/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.html +++ b/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.html @@ -2,3 +2,5 @@ [preprintProvider]="preprintProvider()" [isPreprintProviderLoading]="isPreprintProviderLoading()" /> + + From 2b53d2f18b7eee1a2c270bfb11be9e0ad4195d73 Mon Sep 17 00:00:00 2001 From: Roma Date: Thu, 12 Jun 2025 14:26:41 +0300 Subject: [PATCH 21/34] feat(filters-store): Added new filter to the store --- .../services/preprints-resource-filters.service.ts | 13 ++++++++++++- .../preprints-resources-filters-options.actions.ts | 4 ++++ .../preprints-resources-filters-options.model.ts | 10 +++++++++- .../preprints-resources-filters-options.state.ts | 11 +++++++++++ .../preprints-resources-filters.actions.ts | 9 +++++++++ .../preprints-resources-filters.model.ts | 1 + .../preprints-resources-filters.selectors.ts | 5 +++++ .../preprints-resources-filters.state.ts | 12 ++++++++++++ 8 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/app/features/preprints/services/preprints-resource-filters.service.ts b/src/app/features/preprints/services/preprints-resource-filters.service.ts index b5d294b71..06937b614 100644 --- a/src/app/features/preprints/services/preprints-resource-filters.service.ts +++ b/src/app/features/preprints/services/preprints-resource-filters.service.ts @@ -7,7 +7,14 @@ import { inject, Injectable } from '@angular/core'; import { PreprintsDiscoverSelectors } from '@osf/features/preprints/store/preprints-discover'; import { PreprintsResourcesFiltersSelectors } from '@osf/features/preprints/store/preprints-resources-filters'; import { ResourceFiltersStateModel } from '@osf/features/search/components/resource-filters/store'; -import { Creator, DateCreated, LicenseFilter, ProviderFilter, SubjectFilter } from '@osf/shared/models'; +import { + Creator, + DateCreated, + LicenseFilter, + ProviderFilter, + ResourceTypeFilter, + SubjectFilter, +} from '@osf/shared/models'; import { FiltersOptionsService } from '@osf/shared/services'; import { addFiltersParams, getResourceTypes } from '@osf/shared/utils'; import { ResourceTab } from '@shared/enums'; @@ -50,6 +57,10 @@ export class PreprintsFiltersOptionsService { return this.filtersOptions.getSubjects(this.getParams(), this.getFilterParams()); } + getInstitutions(): Observable { + return this.filtersOptions.getInstitutions(this.getParams(), this.getFilterParams()); + } + getLicenses(): Observable { return this.filtersOptions.getLicenses(this.getParams(), this.getFilterParams()); } diff --git a/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.actions.ts b/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.actions.ts index f3db28408..6546ddf65 100644 --- a/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.actions.ts +++ b/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.actions.ts @@ -12,6 +12,10 @@ export class GetSubjectsOptions { static readonly type = '[Preprints Resource Filters Options] Get Subjects'; } +export class GetInstitutionsOptions { + static readonly type = '[Preprints Resource Filters Options] Get Institutions'; +} + export class GetLicensesOptions { static readonly type = '[Preprints Resource Filters Options] Get Licenses'; } diff --git a/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.model.ts b/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.model.ts index 5a262f47b..50c58382c 100644 --- a/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.model.ts +++ b/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.model.ts @@ -1,4 +1,11 @@ -import { Creator, DateCreated, LicenseFilter, ProviderFilter, SubjectFilter } from '@osf/shared/models'; +import { + Creator, + DateCreated, + InstitutionFilter, + LicenseFilter, + ProviderFilter, + SubjectFilter, +} from '@osf/shared/models'; export interface PreprintsResourceFiltersOptionsStateModel { creators: Creator[]; @@ -6,4 +13,5 @@ export interface PreprintsResourceFiltersOptionsStateModel { subjects: SubjectFilter[]; licenses: LicenseFilter[]; providers: ProviderFilter[]; + institutions: InstitutionFilter[]; } diff --git a/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.state.ts b/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.state.ts index 534491389..6417545be 100644 --- a/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.state.ts +++ b/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.state.ts @@ -10,6 +10,7 @@ import { GetAllOptions, GetCreatorsOptions, GetDatesCreatedOptions, + GetInstitutionsOptions, GetLicensesOptions, GetProvidersOptions, GetSubjectsOptions, @@ -24,6 +25,7 @@ import { PreprintsResourceFiltersOptionsStateModel } from './preprints-resources subjects: [], licenses: [], providers: [], + institutions: [], }, }) @Injectable() @@ -63,6 +65,15 @@ export class PreprintsResourcesFiltersOptionsState { ); } + @Action(GetInstitutionsOptions) + getInstitutions(ctx: StateContext) { + return this.resourceFiltersService.getInstitutions().pipe( + tap((institutions) => { + ctx.patchState({ institutions: institutions }); + }) + ); + } + @Action(GetLicensesOptions) getLicenses(ctx: StateContext) { return this.resourceFiltersService.getLicenses().pipe( diff --git a/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.actions.ts b/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.actions.ts index 35f265c25..3eacd6ad2 100644 --- a/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.actions.ts +++ b/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.actions.ts @@ -22,6 +22,15 @@ export class SetSubject { ) {} } +export class SetInstitution { + static readonly type = '[Preprints Resource Filters] Set Institution'; + + constructor( + public institution: string, + public id: string + ) {} +} + export class SetLicense { static readonly type = '[Preprints Resource Filters] Set License'; diff --git a/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.model.ts b/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.model.ts index f5f236e1a..69bbcb511 100644 --- a/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.model.ts +++ b/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.model.ts @@ -6,4 +6,5 @@ export interface PreprintsResourcesFiltersStateModel { subject: ResourceFilterLabel; license: ResourceFilterLabel; provider: ResourceFilterLabel; + institution: ResourceFilterLabel; } diff --git a/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.selectors.ts b/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.selectors.ts index 7a2a7f916..45b073362 100644 --- a/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.selectors.ts +++ b/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.selectors.ts @@ -33,6 +33,11 @@ export class PreprintsResourcesFiltersSelectors { return state.subject; } + @Selector([PreprintsResourcesFiltersState]) + static getInstitution(state: PreprintsResourcesFiltersStateModel): ResourceFilterLabel { + return state.institution; + } + @Selector([PreprintsResourcesFiltersState]) static getLicense(state: PreprintsResourcesFiltersStateModel): ResourceFilterLabel { return state.license; diff --git a/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.state.ts b/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.state.ts index 7e823e43d..6ea3927fe 100644 --- a/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.state.ts +++ b/src/app/features/preprints/store/preprints-resources-filters/preprints-resources-filters.state.ts @@ -9,6 +9,7 @@ import { ResetFiltersState, SetCreator, SetDateCreated, + SetInstitution, SetLicense, SetProvider, SetSubject, @@ -54,6 +55,17 @@ export class PreprintsResourcesFiltersState { }); } + @Action(SetInstitution) + setInstitution(ctx: StateContext, action: SetInstitution) { + ctx.patchState({ + institution: { + filterName: FilterLabelsModel.institution, + label: action.institution, + value: action.id, + }, + }); + } + @Action(SetLicense) setLicense(ctx: StateContext, action: SetLicense) { ctx.patchState({ From 87c073dcb328b863491e41b9e9227c0635f43467 Mon Sep 17 00:00:00 2001 From: Roma Date: Thu, 12 Jun 2025 14:59:29 +0300 Subject: [PATCH 22/34] fix(style): Fix styles for preprints resources component --- .../preprints-resources/preprints-resources.component.html | 2 +- .../preprints-resources/preprints-resources.component.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/app/features/preprints/components/preprints-resources/preprints-resources.component.html b/src/app/features/preprints/components/preprints-resources/preprints-resources.component.html index 0a8815bb7..98a51588a 100644 --- a/src/app/features/preprints/components/preprints-resources/preprints-resources.component.html +++ b/src/app/features/preprints/components/preprints-resources/preprints-resources.component.html @@ -1,4 +1,4 @@ -
+
@if (resourcesCount() > 10000) {

10 000+ results

diff --git a/src/app/features/preprints/components/preprints-resources/preprints-resources.component.ts b/src/app/features/preprints/components/preprints-resources/preprints-resources.component.ts index 766f9fa98..1d3b7d4c6 100644 --- a/src/app/features/preprints/components/preprints-resources/preprints-resources.component.ts +++ b/src/app/features/preprints/components/preprints-resources/preprints-resources.component.ts @@ -4,7 +4,7 @@ import { DataView } from 'primeng/dataview'; import { Select } from 'primeng/select'; import { NgOptimizedImage } from '@angular/common'; -import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core'; +import { ChangeDetectionStrategy, Component, HostBinding, inject, signal } from '@angular/core'; import { toSignal } from '@angular/core/rxjs-interop'; import { FormsModule } from '@angular/forms'; @@ -37,6 +37,8 @@ import { IS_WEB, IS_XSMALL } from '@shared/utils'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class PreprintsResourcesComponent { + @HostBinding('class') classes = 'flex-1 flex flex-column w-full h-full'; + private readonly actions = createDispatchMap({ setSortBy: SetSortBy, getResourcesByLink: GetResourcesByLink }); searchSortingOptions = searchSortingOptions; From 30f5e3ecf6abc7b931a088b981e0f7fa71ed8608 Mon Sep 17 00:00:00 2001 From: Roma Date: Thu, 12 Jun 2025 15:00:19 +0300 Subject: [PATCH 23/34] feat(preprints-hero): Added ability to pass search control --- .../preprint-provider-hero.component.html | 1 + .../preprint-provider-hero/preprint-provider-hero.component.ts | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/app/features/preprints/components/preprint-provider-hero/preprint-provider-hero.component.html b/src/app/features/preprints/components/preprint-provider-hero/preprint-provider-hero.component.html index 392efc63b..117f77e01 100644 --- a/src/app/features/preprints/components/preprint-provider-hero/preprint-provider-hero.component.html +++ b/src/app/features/preprints/components/preprint-provider-hero/preprint-provider-hero.component.html @@ -57,6 +57,7 @@

{{ preprintProvider()!.name }}

diff --git a/src/app/features/preprints/components/preprint-provider-hero/preprint-provider-hero.component.ts b/src/app/features/preprints/components/preprint-provider-hero/preprint-provider-hero.component.ts index c8d671027..034e63039 100644 --- a/src/app/features/preprints/components/preprint-provider-hero/preprint-provider-hero.component.ts +++ b/src/app/features/preprints/components/preprint-provider-hero/preprint-provider-hero.component.ts @@ -6,6 +6,7 @@ import { Skeleton } from 'primeng/skeleton'; import { NgOptimizedImage } from '@angular/common'; import { ChangeDetectionStrategy, Component, inject, input, output } from '@angular/core'; +import { FormControl } from '@angular/forms'; import { RouterLink } from '@angular/router'; import { PreprintsHelpDialogComponent } from '@osf/features/preprints/components'; @@ -25,6 +26,7 @@ export class PreprintProviderHeroComponent { protected translateService = inject(TranslateService); protected dialogService = inject(DialogService); + searchControl = input(new FormControl()); preprintProvider = input.required(); isPreprintProviderLoading = input.required(); addPreprintClicked = output(); From 089a7b6914627204c0dc1e3ec1fa569ecdd7944a Mon Sep 17 00:00:00 2001 From: Roma Date: Thu, 12 Jun 2025 15:09:54 +0300 Subject: [PATCH 24/34] fix(resource-card): Loading user related counts only for items with ResourceType.Agent --- .../shared/components/resource-card/resource-card.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/shared/components/resource-card/resource-card.component.ts b/src/app/shared/components/resource-card/resource-card.component.ts index d4b251cd7..2a9a2b3d4 100644 --- a/src/app/shared/components/resource-card/resource-card.component.ts +++ b/src/app/shared/components/resource-card/resource-card.component.ts @@ -39,7 +39,7 @@ export class ResourceCardComponent { dataIsLoaded = false; onOpen() { - if (!this.item() || this.dataIsLoaded) { + if (!this.item() || this.dataIsLoaded || this.item().resourceType !== ResourceType.Agent) { return; } From 3c842962510743b01cb3baa293420e504957784a Mon Sep 17 00:00:00 2001 From: Roma Date: Thu, 12 Jun 2025 15:19:08 +0300 Subject: [PATCH 25/34] fix(resource-card): Removed link to my projects for almost all resources --- .../components/resource-card/resource-card.component.html | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/app/shared/components/resource-card/resource-card.component.html b/src/app/shared/components/resource-card/resource-card.component.html index cd142160c..c8c33900a 100644 --- a/src/app/shared/components/resource-card/resource-card.component.html +++ b/src/app/shared/components/resource-card/resource-card.component.html @@ -11,10 +11,9 @@ - +
@@ -54,6 +54,6 @@

{{ 'home.loggedIn.hosting.title' | translate }}

{{ 'home.loggedIn.hosting.subtitle' | translate }}

- +
From d7764aa6ccf61656ef0914eb13882487ff4c54d7 Mon Sep 17 00:00:00 2001 From: Roma Date: Thu, 12 Jun 2025 18:11:05 +0300 Subject: [PATCH 27/34] feat(preprint-filters): Added all filters needed, Implemented discover page logic --- .../preprints-creators-filter.component.html | 16 ++ .../preprints-creators-filter.component.scss | 0 ...reprints-creators-filter.component.spec.ts | 79 ++++++ .../preprints-creators-filter.component.ts | 95 +++++++ ...reprints-institution-filter.component.html | 17 ++ ...reprints-institution-filter.component.scss | 5 + ...rints-institution-filter.component.spec.ts | 86 +++++++ .../preprints-institution-filter.component.ts | 76 ++++++ .../preprints-license-filter.component.html | 17 ++ .../preprints-license-filter.component.scss | 0 ...preprints-license-filter.component.spec.ts | 86 +++++++ .../preprints-license-filter.component.ts | 76 ++++++ .../preprints-subject-filter.component.html | 17 ++ .../preprints-subject-filter.component.scss | 0 ...preprints-subject-filter.component.spec.ts | 54 ++++ .../preprints-subject-filter.component.ts | 76 ++++++ .../features/preprints/components/index.ts | 4 + .../preprints-filter-chips.component.html | 15 ++ .../preprints-filter-chips.component.scss | 11 +- .../preprints-filter-chips.component.ts | 5 + ...preprints-resources-filters.component.html | 48 ++++ ...preprints-resources-filters.component.scss | 16 ++ ...prints-resources-filters.component.spec.ts | 22 ++ .../preprints-resources-filters.component.ts | 71 ++++++ .../preprint-provider-discover.component.html | 1 + .../preprint-provider-discover.component.ts | 238 +++++++++++++++++- ...nts-resources-filters-options.selectors.ts | 14 +- ...eprints-resources-filters-options.state.ts | 1 + 28 files changed, 1133 insertions(+), 13 deletions(-) create mode 100644 src/app/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component.html create mode 100644 src/app/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component.scss create mode 100644 src/app/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component.spec.ts create mode 100644 src/app/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component.ts create mode 100644 src/app/features/preprints/components/filters/preprints-institution-filter/preprints-institution-filter.component.html create mode 100644 src/app/features/preprints/components/filters/preprints-institution-filter/preprints-institution-filter.component.scss create mode 100644 src/app/features/preprints/components/filters/preprints-institution-filter/preprints-institution-filter.component.spec.ts create mode 100644 src/app/features/preprints/components/filters/preprints-institution-filter/preprints-institution-filter.component.ts create mode 100644 src/app/features/preprints/components/filters/preprints-license-filter/preprints-license-filter.component.html create mode 100644 src/app/features/preprints/components/filters/preprints-license-filter/preprints-license-filter.component.scss create mode 100644 src/app/features/preprints/components/filters/preprints-license-filter/preprints-license-filter.component.spec.ts create mode 100644 src/app/features/preprints/components/filters/preprints-license-filter/preprints-license-filter.component.ts create mode 100644 src/app/features/preprints/components/filters/preprints-subject/preprints-subject-filter.component.html create mode 100644 src/app/features/preprints/components/filters/preprints-subject/preprints-subject-filter.component.scss create mode 100644 src/app/features/preprints/components/filters/preprints-subject/preprints-subject-filter.component.spec.ts create mode 100644 src/app/features/preprints/components/filters/preprints-subject/preprints-subject-filter.component.ts create mode 100644 src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.html create mode 100644 src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.scss create mode 100644 src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.spec.ts create mode 100644 src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.ts diff --git a/src/app/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component.html b/src/app/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component.html new file mode 100644 index 000000000..43e89df69 --- /dev/null +++ b/src/app/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component.html @@ -0,0 +1,16 @@ + diff --git a/src/app/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component.scss b/src/app/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component.spec.ts b/src/app/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component.spec.ts new file mode 100644 index 000000000..a4aeab4f3 --- /dev/null +++ b/src/app/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component.spec.ts @@ -0,0 +1,79 @@ +import { Store } from '@ngxs/store'; + +import { MockProvider } from 'ng-mocks'; + +import { SelectChangeEvent } from 'primeng/select'; + +import { signal } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { mockStore } from '@osf/shared/mocks'; +import { Creator } from '@osf/shared/models'; + +import { ResourceFiltersSelectors, SetCreator } from '../../resource-filters/store'; +import { GetAllOptions, ResourceFiltersOptionsSelectors } from '../store'; + +import { PreprintsCreatorsFilterComponent } from './preprints-creators-filter.component'; + +describe('CreatorsFilterComponent', () => { + let component: PreprintsCreatorsFilterComponent; + let fixture: ComponentFixture; + + const store = mockStore; + + const mockCreators: Creator[] = [ + { id: '1', name: 'John Doe' }, + { id: '2', name: 'Jane Smith' }, + { id: '3', name: 'Bob Johnson' }, + ]; + + beforeEach(async () => { + store.selectSignal.mockImplementation((selector) => { + if (selector === ResourceFiltersOptionsSelectors.getCreators) { + return signal(mockCreators); + } + + if (selector === ResourceFiltersSelectors.getCreator) { + return signal({ label: '', value: '' }); + } + + return signal(null); + }); + + await TestBed.configureTestingModule({ + imports: [PreprintsCreatorsFilterComponent], + providers: [MockProvider(Store, store)], + }).compileComponents(); + + fixture = TestBed.createComponent(PreprintsCreatorsFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should initialize with empty input', () => { + expect(component['creatorsInput']()).toBeNull(); + }); + + it('should show all creators when no search text is entered', () => { + const options = component['creatorsOptions'](); + expect(options.length).toBe(3); + expect(options[0].label).toBe('John Doe'); + expect(options[1].label).toBe('Jane Smith'); + expect(options[2].label).toBe('Bob Johnson'); + }); + + it('should set creator when a valid selection is made', () => { + const event = { + originalEvent: { pointerId: 1 } as unknown as PointerEvent, + value: 'John Doe', + } as SelectChangeEvent; + + component.setCreator(event); + expect(store.dispatch).toHaveBeenCalledWith(new SetCreator('John Doe', '1')); + expect(store.dispatch).toHaveBeenCalledWith(GetAllOptions); + }); +}); diff --git a/src/app/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component.ts b/src/app/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component.ts new file mode 100644 index 000000000..2337e2338 --- /dev/null +++ b/src/app/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component.ts @@ -0,0 +1,95 @@ +import { Store } from '@ngxs/store'; + +import { Select, SelectChangeEvent } from 'primeng/select'; + +import { debounceTime, distinctUntilChanged, Subject, takeUntil } from 'rxjs'; + +import { + ChangeDetectionStrategy, + Component, + computed, + effect, + inject, + OnDestroy, + signal, + untracked, +} from '@angular/core'; +import { toObservable } from '@angular/core/rxjs-interop'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; + +import { + PreprintsResourcesFiltersSelectors, + SetCreator, +} from '@osf/features/preprints/store/preprints-resources-filters'; +import { + GetAllOptions, + GetCreatorsOptions, + PreprintsResourcesFiltersOptionsSelectors, +} from '@osf/features/preprints/store/preprints-resources-filters-options'; + +@Component({ + selector: 'osf-preprints-creators-filter', + imports: [Select, ReactiveFormsModule, FormsModule], + templateUrl: './preprints-creators-filter.component.html', + styleUrl: './preprints-creators-filter.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class PreprintsCreatorsFilterComponent implements OnDestroy { + readonly #store = inject(Store); + + protected searchCreatorsResults = this.#store.selectSignal(PreprintsResourcesFiltersOptionsSelectors.getCreators); + protected creatorsOptions = computed(() => { + return this.searchCreatorsResults().map((creator) => ({ + label: creator.name, + id: creator.id, + })); + }); + protected creatorState = this.#store.selectSignal(PreprintsResourcesFiltersSelectors.getCreator); + readonly #unsubscribe = new Subject(); + protected creatorsInput = signal(null); + protected initialization = true; + + constructor() { + toObservable(this.creatorsInput) + .pipe(debounceTime(500), distinctUntilChanged(), takeUntil(this.#unsubscribe)) + .subscribe((searchText) => { + if (!this.initialization) { + if (searchText) { + this.#store.dispatch(new GetCreatorsOptions(searchText ?? '')); + } + + if (!searchText) { + this.#store.dispatch(new SetCreator('', '')); + this.#store.dispatch(GetAllOptions); + } + } else { + this.initialization = false; + } + }); + + effect(() => { + const storeValue = this.creatorState().label; + const currentInput = untracked(() => this.creatorsInput()); + + if (!storeValue && currentInput !== null) { + this.creatorsInput.set(null); + } else if (storeValue && currentInput !== storeValue) { + this.creatorsInput.set(storeValue); + } + }); + } + + ngOnDestroy() { + this.#unsubscribe.complete(); + } + + setCreator(event: SelectChangeEvent): void { + if ((event.originalEvent as PointerEvent).pointerId && event.value) { + const creator = this.creatorsOptions().find((p) => p.label.includes(event.value)); + if (creator) { + this.#store.dispatch(new SetCreator(creator.label, creator.id)); + this.#store.dispatch(GetAllOptions); + } + } + } +} diff --git a/src/app/features/preprints/components/filters/preprints-institution-filter/preprints-institution-filter.component.html b/src/app/features/preprints/components/filters/preprints-institution-filter/preprints-institution-filter.component.html new file mode 100644 index 000000000..0f59afc86 --- /dev/null +++ b/src/app/features/preprints/components/filters/preprints-institution-filter/preprints-institution-filter.component.html @@ -0,0 +1,17 @@ + diff --git a/src/app/features/preprints/components/filters/preprints-institution-filter/preprints-institution-filter.component.scss b/src/app/features/preprints/components/filters/preprints-institution-filter/preprints-institution-filter.component.scss new file mode 100644 index 000000000..5fd36a5f1 --- /dev/null +++ b/src/app/features/preprints/components/filters/preprints-institution-filter/preprints-institution-filter.component.scss @@ -0,0 +1,5 @@ +:host ::ng-deep { + .p-scroller-viewport { + flex: none; + } +} diff --git a/src/app/features/preprints/components/filters/preprints-institution-filter/preprints-institution-filter.component.spec.ts b/src/app/features/preprints/components/filters/preprints-institution-filter/preprints-institution-filter.component.spec.ts new file mode 100644 index 000000000..1561ab883 --- /dev/null +++ b/src/app/features/preprints/components/filters/preprints-institution-filter/preprints-institution-filter.component.spec.ts @@ -0,0 +1,86 @@ +import { Store } from '@ngxs/store'; + +import { MockProvider } from 'ng-mocks'; + +import { SelectChangeEvent } from 'primeng/select'; + +import { signal } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { mockStore } from '@osf/shared/mocks'; +import { InstitutionFilter } from '@osf/shared/models'; + +import { ResourceFiltersSelectors, SetInstitution } from '../../resource-filters/store'; +import { GetAllOptions, ResourceFiltersOptionsSelectors } from '../store'; + +import { PreprintsInstitutionFilterComponent } from './preprints-institution-filter.component'; + +describe('InstitutionFilterComponent', () => { + let component: PreprintsInstitutionFilterComponent; + let fixture: ComponentFixture; + + const store = mockStore; + + const mockInstitutions: InstitutionFilter[] = [ + { id: '1', label: 'Harvard University', count: 15 }, + { id: '2', label: 'MIT', count: 12 }, + { id: '3', label: 'Stanford University', count: 8 }, + ]; + + beforeEach(async () => { + store.selectSignal.mockImplementation((selector) => { + if (selector === ResourceFiltersOptionsSelectors.getInstitutions) { + return signal(mockInstitutions); + } + + if (selector === ResourceFiltersSelectors.getInstitution) { + return signal({ label: '', value: '' }); + } + + return signal(null); + }); + + await TestBed.configureTestingModule({ + imports: [PreprintsInstitutionFilterComponent], + providers: [MockProvider(Store, store)], + }).compileComponents(); + + fixture = TestBed.createComponent(PreprintsInstitutionFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should initialize with empty input text', () => { + expect(component['inputText']()).toBeNull(); + }); + + it('should show all institutions when no search text is entered', () => { + const options = component['institutionsOptions'](); + expect(options.length).toBe(3); + expect(options[0].labelCount).toBe('Harvard University (15)'); + expect(options[1].labelCount).toBe('MIT (12)'); + expect(options[2].labelCount).toBe('Stanford University (8)'); + }); + + it('should filter institutions based on search text', () => { + component['inputText'].set('MIT'); + const options = component['institutionsOptions'](); + expect(options.length).toBe(1); + expect(options[0].labelCount).toBe('MIT (12)'); + }); + + it('should clear institution when selection is cleared', () => { + const event = { + originalEvent: new Event('change'), + value: '', + } as SelectChangeEvent; + + component.setInstitutions(event); + expect(store.dispatch).toHaveBeenCalledWith(new SetInstitution('', '')); + expect(store.dispatch).toHaveBeenCalledWith(GetAllOptions); + }); +}); diff --git a/src/app/features/preprints/components/filters/preprints-institution-filter/preprints-institution-filter.component.ts b/src/app/features/preprints/components/filters/preprints-institution-filter/preprints-institution-filter.component.ts new file mode 100644 index 000000000..c19b7cf56 --- /dev/null +++ b/src/app/features/preprints/components/filters/preprints-institution-filter/preprints-institution-filter.component.ts @@ -0,0 +1,76 @@ +import { Store } from '@ngxs/store'; + +import { Select, SelectChangeEvent } from 'primeng/select'; + +import { ChangeDetectionStrategy, Component, computed, effect, inject, signal, untracked } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { + PreprintsResourcesFiltersSelectors, + SetInstitution, +} from '@osf/features/preprints/store/preprints-resources-filters'; +import { + GetAllOptions, + PreprintsResourcesFiltersOptionsSelectors, +} from '@osf/features/preprints/store/preprints-resources-filters-options'; + +@Component({ + selector: 'osf-preprints-institution-filter', + imports: [Select, FormsModule], + templateUrl: './preprints-institution-filter.component.html', + styleUrl: './preprints-institution-filter.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class PreprintsInstitutionFilterComponent { + readonly #store = inject(Store); + + protected institutionState = this.#store.selectSignal(PreprintsResourcesFiltersSelectors.getInstitution); + protected availableInstitutions = this.#store.selectSignal(PreprintsResourcesFiltersOptionsSelectors.getInstitutions); + protected inputText = signal(null); + protected institutionsOptions = computed(() => { + if (this.inputText() !== null) { + const search = this.inputText()!.toLowerCase(); + return this.availableInstitutions() + .filter((institution) => institution.label.toLowerCase().includes(search)) + .map((institution) => ({ + labelCount: institution.label + ' (' + institution.count + ')', + label: institution.label, + id: institution.id, + })); + } + + return this.availableInstitutions().map((institution) => ({ + labelCount: institution.label + ' (' + institution.count + ')', + label: institution.label, + id: institution.id, + })); + }); + + constructor() { + effect(() => { + const storeValue = this.institutionState().label; + const currentInput = untracked(() => this.inputText()); + + if (!storeValue && currentInput !== null) { + this.inputText.set(null); + } else if (storeValue && currentInput !== storeValue) { + this.inputText.set(storeValue); + } + }); + } + + loading = signal(false); + + setInstitutions(event: SelectChangeEvent): void { + if ((event.originalEvent as PointerEvent).pointerId && event.value) { + const institution = this.institutionsOptions()?.find((institution) => institution.label.includes(event.value)); + if (institution) { + this.#store.dispatch(new SetInstitution(institution.label, institution.id)); + this.#store.dispatch(GetAllOptions); + } + } else { + this.#store.dispatch(new SetInstitution('', '')); + this.#store.dispatch(GetAllOptions); + } + } +} diff --git a/src/app/features/preprints/components/filters/preprints-license-filter/preprints-license-filter.component.html b/src/app/features/preprints/components/filters/preprints-license-filter/preprints-license-filter.component.html new file mode 100644 index 000000000..ce0d34b4d --- /dev/null +++ b/src/app/features/preprints/components/filters/preprints-license-filter/preprints-license-filter.component.html @@ -0,0 +1,17 @@ + diff --git a/src/app/features/preprints/components/filters/preprints-license-filter/preprints-license-filter.component.scss b/src/app/features/preprints/components/filters/preprints-license-filter/preprints-license-filter.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/features/preprints/components/filters/preprints-license-filter/preprints-license-filter.component.spec.ts b/src/app/features/preprints/components/filters/preprints-license-filter/preprints-license-filter.component.spec.ts new file mode 100644 index 000000000..1c320be28 --- /dev/null +++ b/src/app/features/preprints/components/filters/preprints-license-filter/preprints-license-filter.component.spec.ts @@ -0,0 +1,86 @@ +import { Store } from '@ngxs/store'; + +import { MockProvider } from 'ng-mocks'; + +import { SelectChangeEvent } from 'primeng/select'; + +import { signal } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LicenseFilter } from '@osf/shared/models'; + +import { ResourceFiltersSelectors, SetLicense } from '../../resource-filters/store'; +import { GetAllOptions, ResourceFiltersOptionsSelectors } from '../store'; + +import { PreprintsLicenseFilterComponent } from './preprints-license-filter.component'; + +describe('LicenseFilterComponent', () => { + let component: PreprintsLicenseFilterComponent; + let fixture: ComponentFixture; + + const mockStore = { + selectSignal: jest.fn(), + dispatch: jest.fn(), + }; + + const mockLicenses: LicenseFilter[] = [ + { id: '1', label: 'MIT License', count: 10 }, + { id: '2', label: 'Apache License 2.0', count: 5 }, + { id: '3', label: 'GNU GPL v3', count: 3 }, + ]; + + beforeEach(async () => { + mockStore.selectSignal.mockImplementation((selector) => { + if (selector === ResourceFiltersOptionsSelectors.getLicenses) { + return signal(mockLicenses); + } + if (selector === ResourceFiltersSelectors.getLicense) { + return signal({ label: '', value: '' }); + } + return signal(null); + }); + + await TestBed.configureTestingModule({ + imports: [PreprintsLicenseFilterComponent], + providers: [MockProvider(Store, mockStore)], + }).compileComponents(); + + fixture = TestBed.createComponent(PreprintsLicenseFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should initialize with empty input text', () => { + expect(component['inputText']()).toBeNull(); + }); + + it('should show all licenses when no search text is entered', () => { + const options = component['licensesOptions'](); + expect(options.length).toBe(3); + expect(options[0].labelCount).toBe('MIT License (10)'); + expect(options[1].labelCount).toBe('Apache License 2.0 (5)'); + expect(options[2].labelCount).toBe('GNU GPL v3 (3)'); + }); + + it('should filter licenses based on search text', () => { + component['inputText'].set('MIT'); + const options = component['licensesOptions'](); + expect(options.length).toBe(1); + expect(options[0].labelCount).toBe('MIT License (10)'); + }); + + it('should clear license when selection is cleared', () => { + const event = { + originalEvent: new Event('change'), + value: '', + } as SelectChangeEvent; + + component.setLicenses(event); + expect(mockStore.dispatch).toHaveBeenCalledWith(new SetLicense('', '')); + expect(mockStore.dispatch).toHaveBeenCalledWith(GetAllOptions); + }); +}); diff --git a/src/app/features/preprints/components/filters/preprints-license-filter/preprints-license-filter.component.ts b/src/app/features/preprints/components/filters/preprints-license-filter/preprints-license-filter.component.ts new file mode 100644 index 000000000..79c3de5ef --- /dev/null +++ b/src/app/features/preprints/components/filters/preprints-license-filter/preprints-license-filter.component.ts @@ -0,0 +1,76 @@ +import { Store } from '@ngxs/store'; + +import { Select, SelectChangeEvent } from 'primeng/select'; + +import { ChangeDetectionStrategy, Component, computed, effect, inject, signal, untracked } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { + PreprintsResourcesFiltersSelectors, + SetLicense, +} from '@osf/features/preprints/store/preprints-resources-filters'; +import { + GetAllOptions, + PreprintsResourcesFiltersOptionsSelectors, +} from '@osf/features/preprints/store/preprints-resources-filters-options'; + +@Component({ + selector: 'osf-preprints-license-filter', + imports: [Select, FormsModule], + templateUrl: './preprints-license-filter.component.html', + styleUrl: './preprints-license-filter.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class PreprintsLicenseFilterComponent { + readonly #store = inject(Store); + + protected availableLicenses = this.#store.selectSignal(PreprintsResourcesFiltersOptionsSelectors.getLicenses); + protected licenseState = this.#store.selectSignal(PreprintsResourcesFiltersSelectors.getLicense); + protected inputText = signal(null); + protected licensesOptions = computed(() => { + if (this.inputText() !== null) { + const search = this.inputText()!.toLowerCase(); + return this.availableLicenses() + .filter((license) => license.label.toLowerCase().includes(search)) + .map((license) => ({ + labelCount: license.label + ' (' + license.count + ')', + label: license.label, + id: license.id, + })); + } + + return this.availableLicenses().map((license) => ({ + labelCount: license.label + ' (' + license.count + ')', + label: license.label, + id: license.id, + })); + }); + + loading = signal(false); + + constructor() { + effect(() => { + const storeValue = this.licenseState().label; + const currentInput = untracked(() => this.inputText()); + + if (!storeValue && currentInput !== null) { + this.inputText.set(null); + } else if (storeValue && currentInput !== storeValue) { + this.inputText.set(storeValue); + } + }); + } + + setLicenses(event: SelectChangeEvent): void { + if ((event.originalEvent as PointerEvent).pointerId && event.value) { + const license = this.licensesOptions().find((license) => license.label.includes(event.value)); + if (license) { + this.#store.dispatch(new SetLicense(license.label, license.id)); + this.#store.dispatch(GetAllOptions); + } + } else { + this.#store.dispatch(new SetLicense('', '')); + this.#store.dispatch(GetAllOptions); + } + } +} diff --git a/src/app/features/preprints/components/filters/preprints-subject/preprints-subject-filter.component.html b/src/app/features/preprints/components/filters/preprints-subject/preprints-subject-filter.component.html new file mode 100644 index 000000000..a33f1f7af --- /dev/null +++ b/src/app/features/preprints/components/filters/preprints-subject/preprints-subject-filter.component.html @@ -0,0 +1,17 @@ + diff --git a/src/app/features/preprints/components/filters/preprints-subject/preprints-subject-filter.component.scss b/src/app/features/preprints/components/filters/preprints-subject/preprints-subject-filter.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/features/preprints/components/filters/preprints-subject/preprints-subject-filter.component.spec.ts b/src/app/features/preprints/components/filters/preprints-subject/preprints-subject-filter.component.spec.ts new file mode 100644 index 000000000..99d1058aa --- /dev/null +++ b/src/app/features/preprints/components/filters/preprints-subject/preprints-subject-filter.component.spec.ts @@ -0,0 +1,54 @@ +import { Store } from '@ngxs/store'; + +import { MockProvider } from 'ng-mocks'; + +import { of } from 'rxjs'; + +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ResourceFiltersSelectors } from '../../resource-filters/store'; +import { ResourceFiltersOptionsSelectors } from '../store'; + +import { PreprintsSubjectFilterComponent } from './preprints-subject-filter.component'; + +describe('SubjectFilterComponent', () => { + let component: PreprintsSubjectFilterComponent; + let fixture: ComponentFixture; + + const mockSubjects = [ + { id: '1', label: 'Physics', count: 10 }, + { id: '2', label: 'Chemistry', count: 15 }, + { id: '3', label: 'Biology', count: 20 }, + ]; + + const mockStore = { + selectSignal: jest.fn().mockImplementation((selector) => { + if (selector === ResourceFiltersOptionsSelectors.getSubjects) { + return () => mockSubjects; + } + if (selector === ResourceFiltersSelectors.getSubject) { + return () => ({ label: '', id: '' }); + } + return () => null; + }), + dispatch: jest.fn().mockReturnValue(of({})), + }; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PreprintsSubjectFilterComponent], + providers: [MockProvider(Store, mockStore)], + }).compileComponents(); + + fixture = TestBed.createComponent(PreprintsSubjectFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create and initialize with subjects', () => { + expect(component).toBeTruthy(); + expect(component['availableSubjects']()).toEqual(mockSubjects); + expect(component['subjectsOptions']().length).toBe(3); + expect(component['subjectsOptions']()[0].labelCount).toBe('Physics (10)'); + }); +}); diff --git a/src/app/features/preprints/components/filters/preprints-subject/preprints-subject-filter.component.ts b/src/app/features/preprints/components/filters/preprints-subject/preprints-subject-filter.component.ts new file mode 100644 index 000000000..3eaed3498 --- /dev/null +++ b/src/app/features/preprints/components/filters/preprints-subject/preprints-subject-filter.component.ts @@ -0,0 +1,76 @@ +import { Store } from '@ngxs/store'; + +import { Select, SelectChangeEvent } from 'primeng/select'; + +import { ChangeDetectionStrategy, Component, computed, effect, inject, signal, untracked } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { + PreprintsResourcesFiltersSelectors, + SetSubject, +} from '@osf/features/preprints/store/preprints-resources-filters'; +import { + GetAllOptions, + PreprintsResourcesFiltersOptionsSelectors, +} from '@osf/features/preprints/store/preprints-resources-filters-options'; + +@Component({ + selector: 'osf-preprints-subject-filter', + imports: [Select, FormsModule], + templateUrl: './preprints-subject-filter.component.html', + styleUrl: './preprints-subject-filter.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class PreprintsSubjectFilterComponent { + readonly #store = inject(Store); + + protected availableSubjects = this.#store.selectSignal(PreprintsResourcesFiltersOptionsSelectors.getSubjects); + protected subjectState = this.#store.selectSignal(PreprintsResourcesFiltersSelectors.getSubject); + protected inputText = signal(null); + protected subjectsOptions = computed(() => { + if (this.inputText() !== null) { + const search = this.inputText()!.toLowerCase(); + return this.availableSubjects() + .filter((subject) => subject.label.toLowerCase().includes(search)) + .map((subject) => ({ + labelCount: subject.label + ' (' + subject.count + ')', + label: subject.label, + id: subject.id, + })); + } + + return this.availableSubjects().map((subject) => ({ + labelCount: subject.label + ' (' + subject.count + ')', + label: subject.label, + id: subject.id, + })); + }); + + loading = signal(false); + + constructor() { + effect(() => { + const storeValue = this.subjectState().label; + const currentInput = untracked(() => this.inputText()); + + if (!storeValue && currentInput !== null) { + this.inputText.set(null); + } else if (storeValue && currentInput !== storeValue) { + this.inputText.set(storeValue); + } + }); + } + + setSubject(event: SelectChangeEvent): void { + if ((event.originalEvent as PointerEvent).pointerId && event.value) { + const subject = this.subjectsOptions().find((p) => p.label.includes(event.value)); + if (subject) { + this.#store.dispatch(new SetSubject(subject.label, subject.id)); + this.#store.dispatch(GetAllOptions); + } + } else { + this.#store.dispatch(new SetSubject('', '')); + this.#store.dispatch(GetAllOptions); + } + } +} diff --git a/src/app/features/preprints/components/index.ts b/src/app/features/preprints/components/index.ts index 455c300b4..5a04d944f 100644 --- a/src/app/features/preprints/components/index.ts +++ b/src/app/features/preprints/components/index.ts @@ -1,4 +1,8 @@ export { BrowseBySubjectsComponent } from './browse-by-subjects/browse-by-subjects.component'; export { PreprintServicesComponent } from './preprint-services/preprint-services.component'; export { AdvisoryBoardComponent } from '@osf/features/preprints/components/advisory-board/advisory-board.component'; +export { PreprintsDateCreatedFilterComponent } from '@osf/features/preprints/components/filters/preprints-date-created-filter/preprints-date-created-filter.component'; +export { PreprintsFilterChipsComponent } from '@osf/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component'; export { PreprintsHelpDialogComponent } from '@osf/features/preprints/components/preprints-help-dialog/preprints-help-dialog.component'; +export { PreprintsResourcesComponent } from '@osf/features/preprints/components/preprints-resources/preprints-resources.component'; +export { PreprintsResourcesFiltersComponent } from '@osf/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component'; diff --git a/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.html b/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.html index d4c362145..49f9a88cc 100644 --- a/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.html +++ b/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.html @@ -43,6 +43,21 @@ } +@if (filters().institution.value) { + @let institution = filters().institution.filterName + ': ' + filters().institution.label; + + + + + +} + @if (filters().license.value) { @let license = filters().license.filterName + ': ' + filters().license.label; diff --git a/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.scss b/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.scss index ecd03d738..9ff3d3c87 100644 --- a/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.scss +++ b/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.scss @@ -1,15 +1,16 @@ +@use "assets/styles/mixins" as mix; +@use "assets/styles/variables" as var; + :host { display: flex; flex-direction: column; - gap: 0.4rem; + gap: mix.rem(6px); - @media (max-width: 1200px) { + @media (max-width: var.$breakpoint-xl) { flex-direction: row; } - @media (max-width: 600px) { + @media (max-width: var.$breakpoint-sm) { flex-direction: column; } } - -//TODO diff --git a/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.ts b/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.ts index 8132ef171..d875fe69a 100644 --- a/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.ts +++ b/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.ts @@ -9,6 +9,7 @@ import { PreprintsResourcesFiltersSelectors, SetCreator, SetDateCreated, + SetInstitution, SetLicense, SetProvider, SetSubject, @@ -29,6 +30,7 @@ export class PreprintsFilterChipsComponent { setCreator: SetCreator, setDateCreated: SetDateCreated, setSubject: SetSubject, + setInstitution: SetInstitution, setLicense: SetLicense, setProvider: SetProvider, getAllOptions: GetAllOptions, @@ -47,6 +49,9 @@ export class PreprintsFilterChipsComponent { case FilterType.Subject: this.actions.setSubject('', ''); break; + case FilterType.Institution: + this.actions.setInstitution('', ''); + break; case FilterType.License: this.actions.setLicense('', ''); break; diff --git a/src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.html b/src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.html new file mode 100644 index 000000000..ecffb0e26 --- /dev/null +++ b/src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.html @@ -0,0 +1,48 @@ +@if (anyOptionsCount()) { +
+ + + Creator + + + + + + @if (datesOptionsCount() > 0) { + + Date Created + + + + + } + + @if (subjectOptionsCount() > 0) { + + Subject + + + + + } + + @if (licenseOptionsCount() > 0) { + + License + + + + + } + + @if (institutionOptionsCount() > 0) { + + Institution + + + + + } + +
+} diff --git a/src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.scss b/src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.scss new file mode 100644 index 000000000..1dd7a98c8 --- /dev/null +++ b/src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.scss @@ -0,0 +1,16 @@ +@use "assets/styles/variables" as var; +@use "assets/styles/mixins" as mix; + +:host { + width: 30%; + + .filters { + border: 1px solid var.$grey-2; + border-radius: mix.rem(12px); + padding: 0 mix.rem(24px) 0 mix.rem(24px); + display: flex; + flex-direction: column; + row-gap: mix.rem(12px); + height: fit-content; + } +} diff --git a/src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.spec.ts b/src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.spec.ts new file mode 100644 index 000000000..1f8c6791e --- /dev/null +++ b/src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PreprintsResourcesFiltersComponent } from './preprints-resources-filters.component'; + +describe('PreprintsResourcesFiltersComponent', () => { + let component: PreprintsResourcesFiltersComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PreprintsResourcesFiltersComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(PreprintsResourcesFiltersComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.ts b/src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.ts new file mode 100644 index 000000000..bff1258c2 --- /dev/null +++ b/src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.ts @@ -0,0 +1,71 @@ +import { select } from '@ngxs/store'; + +import { Accordion, AccordionContent, AccordionHeader, AccordionPanel } from 'primeng/accordion'; + +import { ChangeDetectionStrategy, Component, computed } from '@angular/core'; + +import { PreprintsDateCreatedFilterComponent } from '@osf/features/preprints/components'; +import { PreprintsCreatorsFilterComponent } from '@osf/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component'; +import { PreprintsInstitutionFilterComponent } from '@osf/features/preprints/components/filters/preprints-institution-filter/preprints-institution-filter.component'; +import { PreprintsLicenseFilterComponent } from '@osf/features/preprints/components/filters/preprints-license-filter/preprints-license-filter.component'; +import { PreprintsSubjectFilterComponent } from '@osf/features/preprints/components/filters/preprints-subject/preprints-subject-filter.component'; +import { PreprintsResourcesFiltersOptionsSelectors } from '@osf/features/preprints/store/preprints-resources-filters-options'; + +@Component({ + selector: 'osf-preprints-resources-filters', + imports: [ + Accordion, + AccordionPanel, + AccordionHeader, + AccordionContent, + PreprintsDateCreatedFilterComponent, + PreprintsCreatorsFilterComponent, + PreprintsSubjectFilterComponent, + PreprintsInstitutionFilterComponent, + PreprintsLicenseFilterComponent, + ], + templateUrl: './preprints-resources-filters.component.html', + styleUrl: './preprints-resources-filters.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class PreprintsResourcesFiltersComponent { + datesCreated = select(PreprintsResourcesFiltersOptionsSelectors.getDatesCreated); + datesOptionsCount = computed(() => { + if (!this.datesCreated()) { + return 0; + } + + return this.datesCreated().reduce((acc, date) => acc + date.count, 0); + }); + + subjectOptions = select(PreprintsResourcesFiltersOptionsSelectors.getSubjects); + subjectOptionsCount = computed(() => { + if (!this.subjectOptions()) { + return 0; + } + + return this.subjectOptions().reduce((acc, item) => acc + item.count, 0); + }); + + institutionOptions = select(PreprintsResourcesFiltersOptionsSelectors.getInstitutions); + institutionOptionsCount = computed(() => { + if (!this.institutionOptions()) { + return 0; + } + + return this.institutionOptions().reduce((acc, item) => acc + item.count, 0); + }); + + licenseOptions = select(PreprintsResourcesFiltersOptionsSelectors.getLicenses); + licenseOptionsCount = computed(() => { + if (!this.licenseOptions()) { + return 0; + } + + return this.licenseOptions().reduce((acc, item) => acc + item.count, 0); + }); + + anyOptionsCount = computed(() => { + return this.datesOptionsCount() > 0; + }); +} diff --git a/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.html b/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.html index 52fe2811e..42a4ec3a3 100644 --- a/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.html +++ b/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.html @@ -1,6 +1,7 @@ diff --git a/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.ts b/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.ts index 1dac0071f..604515a49 100644 --- a/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.ts +++ b/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.ts @@ -1,33 +1,89 @@ import { createDispatchMap, select } from '@ngxs/store'; -import { map, of } from 'rxjs'; +import { debounceTime, map, of, skip, take } from 'rxjs'; -import { ChangeDetectionStrategy, Component, effect, inject, OnDestroy, OnInit } from '@angular/core'; -import { toSignal } from '@angular/core/rxjs-interop'; -import { ActivatedRoute } from '@angular/router'; +import { + ChangeDetectionStrategy, + Component, + DestroyRef, + effect, + HostBinding, + inject, + OnDestroy, + OnInit, + untracked, +} from '@angular/core'; +import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop'; +import { FormControl } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; +import { PreprintsResourcesComponent } from '@osf/features/preprints/components'; import { PreprintProviderHeroComponent } from '@osf/features/preprints/components/preprint-provider-hero/preprint-provider-hero.component'; import { BrandService } from '@osf/features/preprints/services'; import { GetPreprintProviderById, PreprintsSelectors } from '@osf/features/preprints/store/preprints'; +import { GetResources, ResetState, SetSearchText, SetSortBy } from '@osf/features/preprints/store/preprints-discover'; +import { PreprintsDiscoverSelectors } from '@osf/features/preprints/store/preprints-discover/preprints-discover.selectors'; +import { + PreprintsResourcesFiltersSelectors, + ResetFiltersState, + SetCreator, + SetDateCreated, + SetInstitution, + SetLicense, + SetProvider, + SetSubject, +} from '@osf/features/preprints/store/preprints-resources-filters'; +import { GetAllOptions } from '@osf/features/preprints/store/preprints-resources-filters-options'; +import { FilterLabelsModel, ResourceFilterLabel } from '@shared/models'; import { HeaderStyleHelper } from '@shared/utils'; @Component({ selector: 'osf-preprint-provider-discover', - imports: [PreprintProviderHeroComponent], + imports: [PreprintProviderHeroComponent, PreprintsResourcesComponent], templateUrl: './preprint-provider-discover.component.html', styleUrl: './preprint-provider-discover.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, }) export class PreprintProviderDiscoverComponent implements OnInit, OnDestroy { - private readonly route = inject(ActivatedRoute); - private providerId = toSignal(this.route.params.pipe(map((params) => params['providerId'])) ?? of(undefined)); + @HostBinding('class') classes = 'flex-1 flex flex-column w-full h-full'; + private readonly activatedRoute = inject(ActivatedRoute); + private readonly router = inject(Router); + private readonly destroyRef = inject(DestroyRef); + + private providerId = toSignal( + this.activatedRoute.params.pipe(map((params) => params['providerId'])) ?? of(undefined) + ); private actions = createDispatchMap({ getPreprintProviderById: GetPreprintProviderById, + setCreator: SetCreator, + setDateCreated: SetDateCreated, + setSubject: SetSubject, + setInstitution: SetInstitution, + setLicense: SetLicense, + setProvider: SetProvider, + setSearchText: SetSearchText, + setSortBy: SetSortBy, + getAllOptions: GetAllOptions, + getResources: GetResources, + resetFiltersState: ResetFiltersState, + resetDiscoverState: ResetState, }); + + searchControl = new FormControl(''); + preprintProvider = select(PreprintsSelectors.getPreprintProviderDetails(this.providerId())); isPreprintProviderLoading = select(PreprintsSelectors.isPreprintProviderDetailsLoading); + creatorSelected = select(PreprintsResourcesFiltersSelectors.getCreator); + dateCreatedSelected = select(PreprintsResourcesFiltersSelectors.getDateCreated); + subjectSelected = select(PreprintsResourcesFiltersSelectors.getSubject); + licenseSelected = select(PreprintsResourcesFiltersSelectors.getLicense); + providerSelected = select(PreprintsResourcesFiltersSelectors.getProvider); + institutionSelected = select(PreprintsResourcesFiltersSelectors.getInstitution); + sortSelected = select(PreprintsDiscoverSelectors.getSortBy); + searchValue = select(PreprintsDiscoverSelectors.getSearchText); + constructor() { effect(() => { const provider = this.preprintProvider(); @@ -41,14 +97,182 @@ export class PreprintProviderDiscoverComponent implements OnInit, OnDestroy { ); } }); + + // if new value for some filter was put in store, add it to route + effect(() => this.syncFilterToQuery('Creator', this.creatorSelected())); + effect(() => this.syncFilterToQuery('DateCreated', this.dateCreatedSelected())); + effect(() => this.syncFilterToQuery('Subject', this.subjectSelected())); + effect(() => this.syncFilterToQuery('License', this.licenseSelected())); + effect(() => this.syncFilterToQuery('Provider', this.providerSelected())); + effect(() => this.syncFilterToQuery('Institution', this.institutionSelected())); + effect(() => this.syncSortingToQuery(this.sortSelected())); + effect(() => this.syncSearchToQuery(this.searchValue())); + + // if new value for some filter was put in store, fetch resources + effect(() => { + this.creatorSelected(); + this.dateCreatedSelected(); + this.subjectSelected(); + this.licenseSelected(); + this.providerSelected(); + this.sortSelected(); + this.searchValue(); + this.actions.getResources(); + }); + + this.configureSearchControl(); } ngOnInit() { this.actions.getPreprintProviderById(this.providerId()); + + // set all query parameters from route to store when page is loaded + this.activatedRoute.queryParamMap.pipe(take(1)).subscribe((params) => { + const activeFilters = params.get('activeFilters'); + const filters = activeFilters ? JSON.parse(activeFilters) : []; + const sortBy = params.get('sortBy'); + const search = params.get('search'); + + const creator = filters.find((p: ResourceFilterLabel) => p.filterName === FilterLabelsModel.creator); + const dateCreated = filters.find((p: ResourceFilterLabel) => p.filterName === 'DateCreated'); + const subject = filters.find((p: ResourceFilterLabel) => p.filterName === FilterLabelsModel.subject); + const license = filters.find((p: ResourceFilterLabel) => p.filterName === FilterLabelsModel.license); + const provider = filters.find((p: ResourceFilterLabel) => p.filterName === FilterLabelsModel.provider); + const institution = filters.find((p: ResourceFilterLabel) => p.filterName === FilterLabelsModel.institution); + + if (creator) { + this.actions.setCreator(creator.label, creator.value); + } + if (dateCreated) { + this.actions.setDateCreated(dateCreated.value); + } + if (subject) { + this.actions.setSubject(subject.label, subject.value); + } + if (institution) { + this.actions.setInstitution(institution.label, institution.value); + } + if (license) { + this.actions.setLicense(license.label, license.value); + } + if (provider) { + this.actions.setProvider(provider.label, provider.value); + } + if (sortBy) { + this.actions.setSortBy(sortBy); + } + if (search) { + this.actions.setSearchText(search); + } + + this.actions.getAllOptions(); + }); } ngOnDestroy() { HeaderStyleHelper.resetToDefaults(); BrandService.resetBranding(); + this.actions.resetFiltersState(); + this.actions.resetDiscoverState(); + } + + syncFilterToQuery(filterName: string, filterValue: ResourceFilterLabel) { + const paramMap = this.activatedRoute.snapshot.queryParamMap; + const currentParams = { ...this.activatedRoute.snapshot.queryParams }; + + // Read existing parameters + const currentFiltersRaw = paramMap.get('activeFilters'); + + let filters: ResourceFilterLabel[] = []; + + try { + filters = currentFiltersRaw ? (JSON.parse(currentFiltersRaw) as ResourceFilterLabel[]) : []; + } catch (e) { + console.error('Invalid activeFilters format in query params', e); + } + + const index = filters.findIndex((f) => f.filterName === filterName); + + const hasValue = !!filterValue?.value; + + // Update activeFilters array + if (!hasValue && index !== -1) { + filters.splice(index, 1); + } else if (hasValue && filterValue?.label) { + const newFilter = { + filterName, + label: filterValue.label, + value: filterValue.value, + }; + + if (index !== -1) { + filters[index] = newFilter; + } else { + filters.push(newFilter); + } + } + + if (filters.length > 0) { + currentParams['activeFilters'] = JSON.stringify(filters); + } else { + delete currentParams['activeFilters']; + } + + // Navigation + this.router.navigate([], { + relativeTo: this.activatedRoute, + queryParams: currentParams, + replaceUrl: true, + }); + } + + syncSortingToQuery(sortBy: string) { + const currentParams = { ...this.activatedRoute.snapshot.queryParams }; + + if (sortBy && sortBy !== '-relevance') { + currentParams['sortBy'] = sortBy; + } else if (sortBy && sortBy === '-relevance') { + delete currentParams['sortBy']; + } + + this.router.navigate([], { + relativeTo: this.activatedRoute, + queryParams: currentParams, + replaceUrl: true, + }); + } + + syncSearchToQuery(search: string) { + const currentParams = { ...this.activatedRoute.snapshot.queryParams }; + + if (search) { + currentParams['search'] = search; + } else { + delete currentParams['search']; + } + + this.router.navigate([], { + relativeTo: this.activatedRoute, + queryParams: currentParams, + replaceUrl: true, + }); + } + + private configureSearchControl() { + this.searchControl.valueChanges + .pipe(skip(1), debounceTime(500), takeUntilDestroyed(this.destroyRef)) + .subscribe((searchText) => { + this.actions.setSearchText(searchText ?? ''); + this.actions.getAllOptions(); + }); + + effect(() => { + const storeValue = this.searchValue(); + const currentInput = untracked(() => this.searchControl.value); + + if (storeValue && currentInput !== storeValue) { + this.searchControl.setValue(storeValue); + } + }); } } diff --git a/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.selectors.ts b/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.selectors.ts index a75c7ed05..ebc3936fa 100644 --- a/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.selectors.ts +++ b/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.selectors.ts @@ -1,6 +1,13 @@ import { Selector } from '@ngxs/store'; -import { Creator, DateCreated, LicenseFilter, ProviderFilter, SubjectFilter } from '@osf/shared/models'; +import { + Creator, + DateCreated, + InstitutionFilter, + LicenseFilter, + ProviderFilter, + SubjectFilter, +} from '@osf/shared/models'; import { PreprintsResourceFiltersOptionsStateModel } from './preprints-resources-filters-options.model'; import { PreprintsResourcesFiltersOptionsState } from './preprints-resources-filters-options.state'; @@ -31,6 +38,11 @@ export class PreprintsResourcesFiltersOptionsSelectors { return state.subjects; } + @Selector([PreprintsResourcesFiltersOptionsState]) + static getInstitutions(state: PreprintsResourceFiltersOptionsStateModel): InstitutionFilter[] { + return state.institutions; + } + @Selector([PreprintsResourcesFiltersOptionsState]) static getLicenses(state: PreprintsResourceFiltersOptionsStateModel): LicenseFilter[] { return state.licenses; diff --git a/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.state.ts b/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.state.ts index 6417545be..fcc32bc8c 100644 --- a/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.state.ts +++ b/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.state.ts @@ -98,5 +98,6 @@ export class PreprintsResourcesFiltersOptionsState { this.store.dispatch(GetSubjectsOptions); this.store.dispatch(GetLicensesOptions); this.store.dispatch(GetProvidersOptions); + this.store.dispatch(GetInstitutionsOptions); } } From e7a0eb0605de2658da30e1eb7d9e06d695da4482 Mon Sep 17 00:00:00 2001 From: Roma Date: Thu, 12 Jun 2025 18:19:08 +0300 Subject: [PATCH 28/34] fix(preprint-filters-tests): Fixed imports --- .../preprints-creators-filter.component.spec.ts | 15 ++++++++++----- ...preprints-institution-filter.component.spec.ts | 15 ++++++++++----- .../preprints-license-filter.component.spec.ts | 15 ++++++++++----- .../preprints-subject-filter.component.spec.ts | 8 ++++---- 4 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/app/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component.spec.ts b/src/app/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component.spec.ts index a4aeab4f3..d9768e298 100644 --- a/src/app/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component.spec.ts +++ b/src/app/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component.spec.ts @@ -7,12 +7,17 @@ import { SelectChangeEvent } from 'primeng/select'; import { signal } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { + PreprintsResourcesFiltersSelectors, + SetCreator, +} from '@osf/features/preprints/store/preprints-resources-filters'; +import { + GetAllOptions, + PreprintsResourcesFiltersOptionsSelectors, +} from '@osf/features/preprints/store/preprints-resources-filters-options'; import { mockStore } from '@osf/shared/mocks'; import { Creator } from '@osf/shared/models'; -import { ResourceFiltersSelectors, SetCreator } from '../../resource-filters/store'; -import { GetAllOptions, ResourceFiltersOptionsSelectors } from '../store'; - import { PreprintsCreatorsFilterComponent } from './preprints-creators-filter.component'; describe('CreatorsFilterComponent', () => { @@ -29,11 +34,11 @@ describe('CreatorsFilterComponent', () => { beforeEach(async () => { store.selectSignal.mockImplementation((selector) => { - if (selector === ResourceFiltersOptionsSelectors.getCreators) { + if (selector === PreprintsResourcesFiltersOptionsSelectors.getCreators) { return signal(mockCreators); } - if (selector === ResourceFiltersSelectors.getCreator) { + if (selector === PreprintsResourcesFiltersSelectors.getCreator) { return signal({ label: '', value: '' }); } diff --git a/src/app/features/preprints/components/filters/preprints-institution-filter/preprints-institution-filter.component.spec.ts b/src/app/features/preprints/components/filters/preprints-institution-filter/preprints-institution-filter.component.spec.ts index 1561ab883..1e4944e9d 100644 --- a/src/app/features/preprints/components/filters/preprints-institution-filter/preprints-institution-filter.component.spec.ts +++ b/src/app/features/preprints/components/filters/preprints-institution-filter/preprints-institution-filter.component.spec.ts @@ -7,12 +7,17 @@ import { SelectChangeEvent } from 'primeng/select'; import { signal } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { + PreprintsResourcesFiltersSelectors, + SetInstitution, +} from '@osf/features/preprints/store/preprints-resources-filters'; +import { + GetAllOptions, + PreprintsResourcesFiltersOptionsSelectors, +} from '@osf/features/preprints/store/preprints-resources-filters-options'; import { mockStore } from '@osf/shared/mocks'; import { InstitutionFilter } from '@osf/shared/models'; -import { ResourceFiltersSelectors, SetInstitution } from '../../resource-filters/store'; -import { GetAllOptions, ResourceFiltersOptionsSelectors } from '../store'; - import { PreprintsInstitutionFilterComponent } from './preprints-institution-filter.component'; describe('InstitutionFilterComponent', () => { @@ -29,11 +34,11 @@ describe('InstitutionFilterComponent', () => { beforeEach(async () => { store.selectSignal.mockImplementation((selector) => { - if (selector === ResourceFiltersOptionsSelectors.getInstitutions) { + if (selector === PreprintsResourcesFiltersOptionsSelectors.getInstitutions) { return signal(mockInstitutions); } - if (selector === ResourceFiltersSelectors.getInstitution) { + if (selector === PreprintsResourcesFiltersSelectors.getInstitution) { return signal({ label: '', value: '' }); } diff --git a/src/app/features/preprints/components/filters/preprints-license-filter/preprints-license-filter.component.spec.ts b/src/app/features/preprints/components/filters/preprints-license-filter/preprints-license-filter.component.spec.ts index 1c320be28..c2a5fc63b 100644 --- a/src/app/features/preprints/components/filters/preprints-license-filter/preprints-license-filter.component.spec.ts +++ b/src/app/features/preprints/components/filters/preprints-license-filter/preprints-license-filter.component.spec.ts @@ -7,11 +7,16 @@ import { SelectChangeEvent } from 'primeng/select'; import { signal } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { + PreprintsResourcesFiltersSelectors, + SetLicense, +} from '@osf/features/preprints/store/preprints-resources-filters'; +import { + GetAllOptions, + PreprintsResourcesFiltersOptionsSelectors, +} from '@osf/features/preprints/store/preprints-resources-filters-options'; import { LicenseFilter } from '@osf/shared/models'; -import { ResourceFiltersSelectors, SetLicense } from '../../resource-filters/store'; -import { GetAllOptions, ResourceFiltersOptionsSelectors } from '../store'; - import { PreprintsLicenseFilterComponent } from './preprints-license-filter.component'; describe('LicenseFilterComponent', () => { @@ -31,10 +36,10 @@ describe('LicenseFilterComponent', () => { beforeEach(async () => { mockStore.selectSignal.mockImplementation((selector) => { - if (selector === ResourceFiltersOptionsSelectors.getLicenses) { + if (selector === PreprintsResourcesFiltersOptionsSelectors.getLicenses) { return signal(mockLicenses); } - if (selector === ResourceFiltersSelectors.getLicense) { + if (selector === PreprintsResourcesFiltersSelectors.getLicense) { return signal({ label: '', value: '' }); } return signal(null); diff --git a/src/app/features/preprints/components/filters/preprints-subject/preprints-subject-filter.component.spec.ts b/src/app/features/preprints/components/filters/preprints-subject/preprints-subject-filter.component.spec.ts index 99d1058aa..397b79390 100644 --- a/src/app/features/preprints/components/filters/preprints-subject/preprints-subject-filter.component.spec.ts +++ b/src/app/features/preprints/components/filters/preprints-subject/preprints-subject-filter.component.spec.ts @@ -6,8 +6,8 @@ import { of } from 'rxjs'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ResourceFiltersSelectors } from '../../resource-filters/store'; -import { ResourceFiltersOptionsSelectors } from '../store'; +import { PreprintsResourcesFiltersSelectors } from '@osf/features/preprints/store/preprints-resources-filters'; +import { PreprintsResourcesFiltersOptionsSelectors } from '@osf/features/preprints/store/preprints-resources-filters-options'; import { PreprintsSubjectFilterComponent } from './preprints-subject-filter.component'; @@ -23,10 +23,10 @@ describe('SubjectFilterComponent', () => { const mockStore = { selectSignal: jest.fn().mockImplementation((selector) => { - if (selector === ResourceFiltersOptionsSelectors.getSubjects) { + if (selector === PreprintsResourcesFiltersOptionsSelectors.getSubjects) { return () => mockSubjects; } - if (selector === ResourceFiltersSelectors.getSubject) { + if (selector === PreprintsResourcesFiltersSelectors.getSubject) { return () => ({ label: '', id: '' }); } return () => null; From 6b357bf3eac1a4e1f0be6e359d8615977a00b66b Mon Sep 17 00:00:00 2001 From: Roma Date: Thu, 12 Jun 2025 18:22:00 +0300 Subject: [PATCH 29/34] fix(preprint-resources-filters): Fixed anyOptionsCount signal --- .../preprints-resources-filters.component.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.ts b/src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.ts index bff1258c2..9952cb36c 100644 --- a/src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.ts +++ b/src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.ts @@ -66,6 +66,11 @@ export class PreprintsResourcesFiltersComponent { }); anyOptionsCount = computed(() => { - return this.datesOptionsCount() > 0; + return ( + this.datesOptionsCount() > 0 || + this.subjectOptionsCount() > 0 || + this.licenseOptionsCount() > 0 || + this.institutionOptionsCount() > 0 + ); }); } From cd696e9dc436f05152a0312c8ebd7c2fddf34332 Mon Sep 17 00:00:00 2001 From: Roma Date: Thu, 12 Jun 2025 18:27:02 +0300 Subject: [PATCH 30/34] fix(preprint-filters): Added export to index file, fixed imports --- .../preprints-creators-filter.component.html | 0 .../preprints-creators-filter.component.scss | 0 .../preprints-creators-filter.component.spec.ts | 0 .../preprints-creators-filter.component.ts | 0 src/app/features/preprints/components/index.ts | 4 ++++ .../preprints-resources-filters.component.ts | 12 +++++++----- 6 files changed, 11 insertions(+), 5 deletions(-) rename src/app/features/preprints/components/filters/{preprints-creators => preprints-creators-filter}/preprints-creators-filter.component.html (100%) rename src/app/features/preprints/components/filters/{preprints-creators => preprints-creators-filter}/preprints-creators-filter.component.scss (100%) rename src/app/features/preprints/components/filters/{preprints-creators => preprints-creators-filter}/preprints-creators-filter.component.spec.ts (100%) rename src/app/features/preprints/components/filters/{preprints-creators => preprints-creators-filter}/preprints-creators-filter.component.ts (100%) diff --git a/src/app/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component.html b/src/app/features/preprints/components/filters/preprints-creators-filter/preprints-creators-filter.component.html similarity index 100% rename from src/app/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component.html rename to src/app/features/preprints/components/filters/preprints-creators-filter/preprints-creators-filter.component.html diff --git a/src/app/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component.scss b/src/app/features/preprints/components/filters/preprints-creators-filter/preprints-creators-filter.component.scss similarity index 100% rename from src/app/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component.scss rename to src/app/features/preprints/components/filters/preprints-creators-filter/preprints-creators-filter.component.scss diff --git a/src/app/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component.spec.ts b/src/app/features/preprints/components/filters/preprints-creators-filter/preprints-creators-filter.component.spec.ts similarity index 100% rename from src/app/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component.spec.ts rename to src/app/features/preprints/components/filters/preprints-creators-filter/preprints-creators-filter.component.spec.ts diff --git a/src/app/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component.ts b/src/app/features/preprints/components/filters/preprints-creators-filter/preprints-creators-filter.component.ts similarity index 100% rename from src/app/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component.ts rename to src/app/features/preprints/components/filters/preprints-creators-filter/preprints-creators-filter.component.ts diff --git a/src/app/features/preprints/components/index.ts b/src/app/features/preprints/components/index.ts index 5a04d944f..0aa788de3 100644 --- a/src/app/features/preprints/components/index.ts +++ b/src/app/features/preprints/components/index.ts @@ -1,7 +1,11 @@ export { BrowseBySubjectsComponent } from './browse-by-subjects/browse-by-subjects.component'; export { PreprintServicesComponent } from './preprint-services/preprint-services.component'; export { AdvisoryBoardComponent } from '@osf/features/preprints/components/advisory-board/advisory-board.component'; +export { PreprintsCreatorsFilterComponent } from '@osf/features/preprints/components/filters/preprints-creators-filter/preprints-creators-filter.component'; export { PreprintsDateCreatedFilterComponent } from '@osf/features/preprints/components/filters/preprints-date-created-filter/preprints-date-created-filter.component'; +export { PreprintsInstitutionFilterComponent } from '@osf/features/preprints/components/filters/preprints-institution-filter/preprints-institution-filter.component'; +export { PreprintsLicenseFilterComponent } from '@osf/features/preprints/components/filters/preprints-license-filter/preprints-license-filter.component'; +export { PreprintsSubjectFilterComponent } from '@osf/features/preprints/components/filters/preprints-subject/preprints-subject-filter.component'; export { PreprintsFilterChipsComponent } from '@osf/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component'; export { PreprintsHelpDialogComponent } from '@osf/features/preprints/components/preprints-help-dialog/preprints-help-dialog.component'; export { PreprintsResourcesComponent } from '@osf/features/preprints/components/preprints-resources/preprints-resources.component'; diff --git a/src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.ts b/src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.ts index 9952cb36c..8b9f0ebe4 100644 --- a/src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.ts +++ b/src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.ts @@ -4,11 +4,13 @@ import { Accordion, AccordionContent, AccordionHeader, AccordionPanel } from 'pr import { ChangeDetectionStrategy, Component, computed } from '@angular/core'; -import { PreprintsDateCreatedFilterComponent } from '@osf/features/preprints/components'; -import { PreprintsCreatorsFilterComponent } from '@osf/features/preprints/components/filters/preprints-creators/preprints-creators-filter.component'; -import { PreprintsInstitutionFilterComponent } from '@osf/features/preprints/components/filters/preprints-institution-filter/preprints-institution-filter.component'; -import { PreprintsLicenseFilterComponent } from '@osf/features/preprints/components/filters/preprints-license-filter/preprints-license-filter.component'; -import { PreprintsSubjectFilterComponent } from '@osf/features/preprints/components/filters/preprints-subject/preprints-subject-filter.component'; +import { + PreprintsCreatorsFilterComponent, + PreprintsDateCreatedFilterComponent, + PreprintsInstitutionFilterComponent, + PreprintsLicenseFilterComponent, + PreprintsSubjectFilterComponent, +} from '@osf/features/preprints/components'; import { PreprintsResourcesFiltersOptionsSelectors } from '@osf/features/preprints/store/preprints-resources-filters-options'; @Component({ From b0f2ebdd91ba5213a71dd88052993764e0a67e1b Mon Sep 17 00:00:00 2001 From: Roma Date: Thu, 12 Jun 2025 18:37:47 +0300 Subject: [PATCH 31/34] fix(resource-card): Removed global styles, added styles to shared component --- .../resource-card.component.scss | 92 ++++++++++++++++++- .../styles/components/resource-card.scss | 90 ------------------ 2 files changed, 91 insertions(+), 91 deletions(-) delete mode 100644 src/assets/styles/components/resource-card.scss diff --git a/src/app/shared/components/resource-card/resource-card.component.scss b/src/app/shared/components/resource-card/resource-card.component.scss index 9c202cc5f..572744b3b 100644 --- a/src/app/shared/components/resource-card/resource-card.component.scss +++ b/src/app/shared/components/resource-card/resource-card.component.scss @@ -1 +1,91 @@ -@use "../../../../assets/styles/components/resource-card" as resource-card; +@use "assets/styles/variables" as var; + +.resource { + display: flex; + flex-direction: column; + border: 1px solid var.$grey-2; + border-radius: 8px; + padding: 1.7rem; + row-gap: 0.85rem; + + .title { + font-weight: 700; + font-size: 1.4rem; + line-height: 1.7rem; + color: var.$dark-blue-1; + padding-bottom: 4px; + + &:hover { + text-decoration: underline; + } + } + + span { + display: inline; + } + + a { + font-weight: bold; + display: inline; + overflow: hidden; + text-overflow: clip; + white-space: wrap; + word-wrap: break-word; + overflow-wrap: break-word; + word-break: break-word; + + &:hover { + text-decoration: underline; + } + } + + .orcid-icon { + height: 16px; + } + + .type { + width: fit-content; + background: var.$grey-3; + font-weight: bold; + padding: 4px 12px 4px 12px; + border-radius: 4px; + } + + p { + color: var.$dark-blue-1; + font-weight: 400; + display: inline; + overflow: hidden; + white-space: wrap; + word-wrap: break-word; + overflow-wrap: break-word; + word-break: break-word; + } + + .icon-container { + color: var.$dark-blue-1; + display: flex; + align-items: center; + column-gap: 0.3rem; + + &:hover { + text-decoration: none; + color: var.$pr-blue-1; + } + } + + .description { + line-height: 2rem; + word-wrap: break-word; + overflow-wrap: break-word; + word-break: break-word; + } + + .content { + display: flex; + flex-direction: column; + row-gap: 1.7rem; + border-top: 1px solid var.$grey-2; + padding-top: 1rem; + } +} diff --git a/src/assets/styles/components/resource-card.scss b/src/assets/styles/components/resource-card.scss deleted file mode 100644 index e0a92b726..000000000 --- a/src/assets/styles/components/resource-card.scss +++ /dev/null @@ -1,90 +0,0 @@ -@use "assets/styles/variables" as var; - -.resource { - display: flex; - flex-direction: column; - border: 1px solid var.$grey-2; - border-radius: 8px; - padding: 1.7rem; - row-gap: 0.85rem; - - .title { - font-weight: 700; - font-size: 1.4rem; - line-height: 1.7rem; - color: var.$dark-blue-1; - padding-bottom: 4px; - &:hover { - text-decoration: underline; - } - } - - span { - display: inline; - } - - a { - font-weight: bold; - display: inline; - overflow: hidden; - text-overflow: clip; - white-space: wrap; - word-wrap: break-word; - overflow-wrap: break-word; - word-break: break-word; - - &:hover { - text-decoration: underline; - } - } - - .orcid-icon { - height: 16px; - } - - .type { - width: fit-content; - background: var.$grey-3; - font-weight: bold; - padding: 4px 12px 4px 12px; - border-radius: 4px; - } - - p { - color: var.$dark-blue-1; - font-weight: 400; - display: inline; - overflow: hidden; - white-space: wrap; - word-wrap: break-word; - overflow-wrap: break-word; - word-break: break-word; - } - - .icon-container { - color: var.$dark-blue-1; - display: flex; - align-items: center; - column-gap: 0.3rem; - - &:hover { - text-decoration: none; - color: var.$pr-blue-1; - } - } - - .description { - line-height: 2rem; - word-wrap: break-word; - overflow-wrap: break-word; - word-break: break-word; - } - - .content { - display: flex; - flex-direction: column; - row-gap: 1.7rem; - border-top: 1px solid var.$grey-2; - padding-top: 1rem; - } -} From b183a966869cc6b1b50267b0e1a9571010013437 Mon Sep 17 00:00:00 2001 From: Roma Date: Thu, 12 Jun 2025 19:12:53 +0300 Subject: [PATCH 32/34] feat(preprints-discover): Added iri prop to PreprintProviderDetails, used in search requests --- .../preprints/mappers/preprints.mapper.ts | 1 + .../preprints/models/preprints.models.ts | 4 ++++ .../preprint-provider-discover.component.ts | 19 +++++++++++++++++-- .../preprints-resource-filters.service.ts | 1 + .../preprints-discover.actions.ts | 6 ++++++ .../preprints-discover.model.ts | 1 + .../preprints-discover.selectors.ts | 5 +++++ .../preprints-discover.state.ts | 14 ++++++++++++++ ...eprints-resources-filters-options.state.ts | 4 ++++ 9 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/app/features/preprints/mappers/preprints.mapper.ts b/src/app/features/preprints/mappers/preprints.mapper.ts index 732e7ff8a..5370489cb 100644 --- a/src/app/features/preprints/mappers/preprints.mapper.ts +++ b/src/app/features/preprints/mappers/preprints.mapper.ts @@ -28,6 +28,7 @@ export class PreprintsMapper { primaryColor: brandRaw.attributes.primary_color, secondaryColor: brandRaw.attributes.secondary_color, }, + iri: response.links.iri, }; } diff --git a/src/app/features/preprints/models/preprints.models.ts b/src/app/features/preprints/models/preprints.models.ts index 9c4743a96..3c6fba4a6 100644 --- a/src/app/features/preprints/models/preprints.models.ts +++ b/src/app/features/preprints/models/preprints.models.ts @@ -23,6 +23,7 @@ export interface PreprintProviderDetails { allowSubmissions: boolean; brand: Brand; lastFetched?: number; + iri: string; } export interface PreprintProviderToAdvertise { @@ -60,6 +61,9 @@ export interface PreprintProviderDetailsGetResponse { data: BrandGetResponse; }; }; + links: { + iri: string; + }; } export interface BrandGetResponse { diff --git a/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.ts b/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.ts index 604515a49..d8e442cbe 100644 --- a/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.ts +++ b/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.ts @@ -21,7 +21,13 @@ import { PreprintsResourcesComponent } from '@osf/features/preprints/components' import { PreprintProviderHeroComponent } from '@osf/features/preprints/components/preprint-provider-hero/preprint-provider-hero.component'; import { BrandService } from '@osf/features/preprints/services'; import { GetPreprintProviderById, PreprintsSelectors } from '@osf/features/preprints/store/preprints'; -import { GetResources, ResetState, SetSearchText, SetSortBy } from '@osf/features/preprints/store/preprints-discover'; +import { + GetResources, + ResetState, + SetProviderIri, + SetSearchText, + SetSortBy, +} from '@osf/features/preprints/store/preprints-discover'; import { PreprintsDiscoverSelectors } from '@osf/features/preprints/store/preprints-discover/preprints-discover.selectors'; import { PreprintsResourcesFiltersSelectors, @@ -49,7 +55,7 @@ export class PreprintProviderDiscoverComponent implements OnInit, OnDestroy { private readonly activatedRoute = inject(ActivatedRoute); private readonly router = inject(Router); private readonly destroyRef = inject(DestroyRef); - + private initAfterIniReceived = false; private providerId = toSignal( this.activatedRoute.params.pipe(map((params) => params['providerId'])) ?? of(undefined) ); @@ -68,6 +74,7 @@ export class PreprintProviderDiscoverComponent implements OnInit, OnDestroy { getResources: GetResources, resetFiltersState: ResetFiltersState, resetDiscoverState: ResetState, + setProviderIri: SetProviderIri, }); searchControl = new FormControl(''); @@ -89,6 +96,14 @@ export class PreprintProviderDiscoverComponent implements OnInit, OnDestroy { const provider = this.preprintProvider(); if (provider) { + this.actions.setProviderIri(provider.iri); + + if (!this.initAfterIniReceived) { + this.initAfterIniReceived = true; + this.actions.getResources(); + this.actions.getAllOptions(); + } + BrandService.applyBranding(provider.brand); HeaderStyleHelper.applyHeaderStyles( provider.brand.primaryColor, diff --git a/src/app/features/preprints/services/preprints-resource-filters.service.ts b/src/app/features/preprints/services/preprints-resource-filters.service.ts index 06937b614..5596551b6 100644 --- a/src/app/features/preprints/services/preprints-resource-filters.service.ts +++ b/src/app/features/preprints/services/preprints-resource-filters.service.ts @@ -40,6 +40,7 @@ export class PreprintsFiltersOptionsService { params['cardSearchFilter[resourceType]'] = resourceTypes; params['cardSearchFilter[accessService]'] = 'https://staging4.osf.io/'; params['cardSearchText[*,creator.name,isContainedBy.creator.name]'] = searchText; + params['cardSearchFilter[publisher][]'] = this.store.selectSnapshot(PreprintsDiscoverSelectors.getIri); params['page[size]'] = '10'; params['sort'] = sort; return params; diff --git a/src/app/features/preprints/store/preprints-discover/preprints-discover.actions.ts b/src/app/features/preprints/store/preprints-discover/preprints-discover.actions.ts index 8df945ce4..b488d206e 100644 --- a/src/app/features/preprints/store/preprints-discover/preprints-discover.actions.ts +++ b/src/app/features/preprints/store/preprints-discover/preprints-discover.actions.ts @@ -20,6 +20,12 @@ export class SetSortBy { constructor(public sortBy: string) {} } +export class SetProviderIri { + static readonly type = '[Preprints Discover] Set Provider Iri'; + + constructor(public providerIri: string) {} +} + export class ResetState { static readonly type = '[Preprints Discover] Reset State'; } diff --git a/src/app/features/preprints/store/preprints-discover/preprints-discover.model.ts b/src/app/features/preprints/store/preprints-discover/preprints-discover.model.ts index 15d628071..174ac3465 100644 --- a/src/app/features/preprints/store/preprints-discover/preprints-discover.model.ts +++ b/src/app/features/preprints/store/preprints-discover/preprints-discover.model.ts @@ -2,6 +2,7 @@ import { AsyncStateModel, Resource } from '@shared/models'; export interface PreprintsDiscoverStateModel { resources: AsyncStateModel; + providerIri: string; resourcesCount: number; searchText: string; sortBy: string; diff --git a/src/app/features/preprints/store/preprints-discover/preprints-discover.selectors.ts b/src/app/features/preprints/store/preprints-discover/preprints-discover.selectors.ts index 807c43ca6..e7a5a2a76 100644 --- a/src/app/features/preprints/store/preprints-discover/preprints-discover.selectors.ts +++ b/src/app/features/preprints/store/preprints-discover/preprints-discover.selectors.ts @@ -26,6 +26,11 @@ export class PreprintsDiscoverSelectors { return state.sortBy; } + @Selector([PreprintsDiscoverState]) + static getIri(state: PreprintsDiscoverStateModel): string { + return state.providerIri; + } + @Selector([PreprintsDiscoverState]) static getFirst(state: PreprintsDiscoverStateModel): string { return state.first; diff --git a/src/app/features/preprints/store/preprints-discover/preprints-discover.state.ts b/src/app/features/preprints/store/preprints-discover/preprints-discover.state.ts index 7013ae701..65167f668 100644 --- a/src/app/features/preprints/store/preprints-discover/preprints-discover.state.ts +++ b/src/app/features/preprints/store/preprints-discover/preprints-discover.state.ts @@ -8,6 +8,7 @@ import { GetResources, GetResourcesByLink, ResetState, + SetProviderIri, SetSearchText, SetSortBy, } from '@osf/features/preprints/store/preprints-discover/preprints-discover.actions'; @@ -27,6 +28,7 @@ import { addFiltersParams, getResourceTypes } from '@shared/utils'; isLoading: false, error: null, }, + providerIri: '', resourcesCount: 0, searchText: '', sortBy: '-relevance', @@ -55,6 +57,9 @@ export class PreprintsDiscoverState implements NgxsOnInit { const sortBy = this.store.selectSnapshot(PreprintsDiscoverSelectors.getSortBy); const resourceTab = ResourceTab.Preprints; const resourceTypes = getResourceTypes(resourceTab); + filtersParams['cardSearchFilter[publisher][]'] = this.store.selectSnapshot( + PreprintsDiscoverSelectors.getIri + ); return this.searchService.getResources(filtersParams, searchText, sortBy, resourceTypes).pipe( tap((response) => { @@ -93,6 +98,9 @@ export class PreprintsDiscoverState implements NgxsOnInit { @Action(GetResources) getResources() { + if (!this.store.selectSnapshot(PreprintsDiscoverSelectors.getIri)) { + return; + } this.loadRequests.next({ type: GetResourcesRequestTypeEnum.GetResources, }); @@ -116,6 +124,11 @@ export class PreprintsDiscoverState implements NgxsOnInit { ctx.patchState({ sortBy: action.sortBy }); } + @Action(SetProviderIri) + setProviderIri(ctx: StateContext, action: SetProviderIri) { + ctx.patchState({ providerIri: action.providerIri }); + } + @Action(ResetState) resetState(ctx: StateContext) { ctx.patchState({ @@ -124,6 +137,7 @@ export class PreprintsDiscoverState implements NgxsOnInit { isLoading: false, error: null, }, + providerIri: '', resourcesCount: 0, searchText: '', sortBy: '-relevance', diff --git a/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.state.ts b/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.state.ts index fcc32bc8c..ed9272d16 100644 --- a/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.state.ts +++ b/src/app/features/preprints/store/preprints-resources-filters-options/preprints-resources-filters-options.state.ts @@ -5,6 +5,7 @@ import { tap } from 'rxjs'; import { inject, Injectable } from '@angular/core'; import { PreprintsFiltersOptionsService } from '@osf/features/preprints/services'; +import { PreprintsDiscoverSelectors } from '@osf/features/preprints/store/preprints-discover'; import { GetAllOptions, @@ -94,6 +95,9 @@ export class PreprintsResourcesFiltersOptionsState { @Action(GetAllOptions) getAllOptions() { + if (!this.store.selectSnapshot(PreprintsDiscoverSelectors.getIri)) { + return; + } this.store.dispatch(GetDatesCreatedOptions); this.store.dispatch(GetSubjectsOptions); this.store.dispatch(GetLicensesOptions); From 3d9b8673f59e32e49c929ec9581e4e3a3f99458f Mon Sep 17 00:00:00 2001 From: Roma Date: Thu, 12 Jun 2025 19:17:43 +0300 Subject: [PATCH 33/34] fix(filters-chips): Reordered chips, removed unused --- .../preprints-filter-chips.component.html | 25 ++++--------------- .../preprints-filter-chips.component.ts | 5 ---- 2 files changed, 5 insertions(+), 25 deletions(-) diff --git a/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.html b/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.html index 49f9a88cc..1626a7853 100644 --- a/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.html +++ b/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.html @@ -43,21 +43,6 @@
} -@if (filters().institution.value) { - @let institution = filters().institution.filterName + ': ' + filters().institution.label; - - - - - -} - @if (filters().license.value) { @let license = filters().license.filterName + ': ' + filters().license.label; @@ -73,16 +58,16 @@ } -@if (filters().provider.value) { - @let provider = filters().provider.filterName + ': ' + filters().provider.label; - +@if (filters().institution.value) { + @let institution = filters().institution.filterName + ': ' + filters().institution.label; + diff --git a/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.ts b/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.ts index d875fe69a..f9461b526 100644 --- a/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.ts +++ b/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.ts @@ -11,7 +11,6 @@ import { SetDateCreated, SetInstitution, SetLicense, - SetProvider, SetSubject, } from '@osf/features/preprints/store/preprints-resources-filters'; import { GetAllOptions } from '@osf/features/preprints/store/preprints-resources-filters-options'; @@ -32,7 +31,6 @@ export class PreprintsFilterChipsComponent { setSubject: SetSubject, setInstitution: SetInstitution, setLicense: SetLicense, - setProvider: SetProvider, getAllOptions: GetAllOptions, }); @@ -55,9 +53,6 @@ export class PreprintsFilterChipsComponent { case FilterType.License: this.actions.setLicense('', ''); break; - case FilterType.Provider: - this.actions.setProvider('', ''); - break; } this.actions.getAllOptions(); } From 24b8a54cdbf2ba62b349ac7518577bcabad282e3 Mon Sep 17 00:00:00 2001 From: Roma Date: Fri, 13 Jun 2025 11:53:58 +0300 Subject: [PATCH 34/34] fix(preprints-discover): Minor fixes --- .../preprint-provider-discover.component.html | 1 - .../preprint-provider-discover.component.ts | 3 --- .../preprints/store/preprints/preprints.selectors.ts | 2 +- .../resource-card/resource-card.component.ts | 12 +----------- 4 files changed, 2 insertions(+), 16 deletions(-) diff --git a/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.html b/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.html index 42a4ec3a3..2b00e414b 100644 --- a/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.html +++ b/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.html @@ -3,5 +3,4 @@ [isPreprintProviderLoading]="isPreprintProviderLoading()" [searchControl]="searchControl" /> - diff --git a/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.ts b/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.ts index d8e442cbe..41689ad64 100644 --- a/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.ts +++ b/src/app/features/preprints/pages/preprint-provider-discover/preprint-provider-discover.component.ts @@ -195,7 +195,6 @@ export class PreprintProviderDiscoverComponent implements OnInit, OnDestroy { const paramMap = this.activatedRoute.snapshot.queryParamMap; const currentParams = { ...this.activatedRoute.snapshot.queryParams }; - // Read existing parameters const currentFiltersRaw = paramMap.get('activeFilters'); let filters: ResourceFilterLabel[] = []; @@ -210,7 +209,6 @@ export class PreprintProviderDiscoverComponent implements OnInit, OnDestroy { const hasValue = !!filterValue?.value; - // Update activeFilters array if (!hasValue && index !== -1) { filters.splice(index, 1); } else if (hasValue && filterValue?.label) { @@ -233,7 +231,6 @@ export class PreprintProviderDiscoverComponent implements OnInit, OnDestroy { delete currentParams['activeFilters']; } - // Navigation this.router.navigate([], { relativeTo: this.activatedRoute, queryParams: currentParams, diff --git a/src/app/features/preprints/store/preprints/preprints.selectors.ts b/src/app/features/preprints/store/preprints/preprints.selectors.ts index 4af549b59..29f3e15a0 100644 --- a/src/app/features/preprints/store/preprints/preprints.selectors.ts +++ b/src/app/features/preprints/store/preprints/preprints.selectors.ts @@ -1,6 +1,6 @@ import { Selector } from '@ngxs/store'; -import { PreprintsState, PreprintsStateModel } from '@osf/features/preprints/store/preprints/index'; +import { PreprintsState, PreprintsStateModel } from '@osf/features/preprints/store/preprints'; export class PreprintsSelectors { @Selector([PreprintsState]) diff --git a/src/app/shared/components/resource-card/resource-card.component.ts b/src/app/shared/components/resource-card/resource-card.component.ts index 2a9a2b3d4..c3ee18a79 100644 --- a/src/app/shared/components/resource-card/resource-card.component.ts +++ b/src/app/shared/components/resource-card/resource-card.component.ts @@ -6,7 +6,6 @@ import { finalize } from 'rxjs'; import { DatePipe, NgOptimizedImage } from '@angular/common'; import { ChangeDetectionStrategy, Component, inject, model } from '@angular/core'; import { toSignal } from '@angular/core/rxjs-interop'; -import { RouterLink } from '@angular/router'; import { ResourceType } from '@shared/enums'; import { Resource } from '@shared/models'; @@ -15,16 +14,7 @@ import { IS_XSMALL } from '@shared/utils'; @Component({ selector: 'osf-resource-card', - imports: [ - Accordion, - AccordionContent, - AccordionHeader, - AccordionPanel, - DatePipe, - NgOptimizedImage, - Skeleton, - RouterLink, - ], + imports: [Accordion, AccordionContent, AccordionHeader, AccordionPanel, DatePipe, NgOptimizedImage, Skeleton], templateUrl: './resource-card.component.html', styleUrl: './resource-card.component.scss', changeDetection: ChangeDetectionStrategy.OnPush,