From 18d44e79e8fbc18eeaaa45ca0434ba2c5b53d4b6 Mon Sep 17 00:00:00 2001 From: Roma Date: Thu, 3 Jul 2025 11:18:54 +0300 Subject: [PATCH 01/18] refactor(preprints-state): Renamed Preprints -> PreprintProviders --- .../preprints/constants/preprints.routes.ts | 4 +- .../landing/preprints-landing.component.ts | 14 +++---- .../preprint-provider-discover.component.ts | 6 +-- .../preprint-provider-overview.component.ts | 12 +++--- .../select-preprint-service.component.ts | 9 ++-- .../submit-preprint-stepper.component.ts | 6 +-- .../store/preprint-providers/index.ts | 4 ++ .../preprint-providers.actions.ts} | 0 .../preprint-providers.model.ts} | 2 +- .../preprint-providers.selectors.ts | 41 +++++++++++++++++++ .../preprint-providers.state.ts} | 26 +++++++----- .../preprints/store/preprints/index.ts | 4 -- .../store/preprints/preprints.selectors.ts | 41 ------------------- 13 files changed, 88 insertions(+), 81 deletions(-) create mode 100644 src/app/features/preprints/store/preprint-providers/index.ts rename src/app/features/preprints/store/{preprints/preprints.actions.ts => preprint-providers/preprint-providers.actions.ts} (100%) rename src/app/features/preprints/store/{preprints/preprints.model.ts => preprint-providers/preprint-providers.model.ts} (90%) create mode 100644 src/app/features/preprints/store/preprint-providers/preprint-providers.selectors.ts rename src/app/features/preprints/store/{preprints/preprints.state.ts => preprint-providers/preprint-providers.state.ts} (87%) delete mode 100644 src/app/features/preprints/store/preprints/index.ts delete mode 100644 src/app/features/preprints/store/preprints/preprints.selectors.ts diff --git a/src/app/features/preprints/constants/preprints.routes.ts b/src/app/features/preprints/constants/preprints.routes.ts index 3d039e468..f1ac2a629 100644 --- a/src/app/features/preprints/constants/preprints.routes.ts +++ b/src/app/features/preprints/constants/preprints.routes.ts @@ -3,7 +3,7 @@ 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 { PreprintProvidersState } from '@osf/features/preprints/store/preprint-providers'; 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'; @@ -16,7 +16,7 @@ export const preprintsRoutes: Routes = [ component: PreprintsComponent, providers: [ provideStates([ - PreprintsState, + PreprintProvidersState, PreprintsDiscoverState, PreprintsResourcesFiltersState, PreprintsResourcesFiltersOptionsState, 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 0990c38b5..d8f4c060a 100644 --- a/src/app/features/preprints/pages/landing/preprints-landing.component.ts +++ b/src/app/features/preprints/pages/landing/preprints-landing.component.ts @@ -19,8 +19,8 @@ import { GetHighlightedSubjectsByProviderId, GetPreprintProviderById, GetPreprintProvidersToAdvertise, - PreprintsSelectors, -} from '@osf/features/preprints/store/preprints'; + PreprintProvidersSelectors, +} from '@osf/features/preprints/store/preprint-providers'; import { SearchInputComponent } from '@shared/components'; import { ResourceTab } from '@shared/enums'; import { BrandService } from '@shared/services'; @@ -55,11 +55,11 @@ export class PreprintsLandingComponent implements OnInit, OnDestroy { getHighlightedSubjectsByProviderId: GetHighlightedSubjectsByProviderId, }); - osfPreprintProvider = select(PreprintsSelectors.getPreprintProviderDetails(this.OSF_PROVIDER_ID)); - isPreprintProviderLoading = select(PreprintsSelectors.isPreprintProviderDetailsLoading); - preprintProvidersToAdvertise = select(PreprintsSelectors.getPreprintProvidersToAdvertise); - highlightedSubjectsByProviderId = select(PreprintsSelectors.getHighlightedSubjectsForProvider); - areSubjectsLoading = select(PreprintsSelectors.areSubjectsLoading); + osfPreprintProvider = select(PreprintProvidersSelectors.getPreprintProviderDetails(this.OSF_PROVIDER_ID)); + isPreprintProviderLoading = select(PreprintProvidersSelectors.isPreprintProviderDetailsLoading); + preprintProvidersToAdvertise = select(PreprintProvidersSelectors.getPreprintProvidersToAdvertise); + highlightedSubjectsByProviderId = select(PreprintProvidersSelectors.getHighlightedSubjectsForProvider); + areSubjectsLoading = select(PreprintProvidersSelectors.areSubjectsLoading); constructor() { effect(() => { 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 f1425fa2c..a35e60a4d 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 @@ -19,7 +19,7 @@ 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 { GetPreprintProviderById, PreprintsSelectors } from '@osf/features/preprints/store/preprints'; +import { GetPreprintProviderById, PreprintProvidersSelectors } from '@osf/features/preprints/store/preprint-providers'; import { GetResources, ResetState, @@ -79,8 +79,8 @@ export class PreprintProviderDiscoverComponent implements OnInit, OnDestroy { searchControl = new FormControl(''); - preprintProvider = select(PreprintsSelectors.getPreprintProviderDetails(this.providerId())); - isPreprintProviderLoading = select(PreprintsSelectors.isPreprintProviderDetailsLoading); + preprintProvider = select(PreprintProvidersSelectors.getPreprintProviderDetails(this.providerId())); + isPreprintProviderLoading = select(PreprintProvidersSelectors.isPreprintProviderDetailsLoading); creatorSelected = select(PreprintsResourcesFiltersSelectors.getCreator); dateCreatedSelected = select(PreprintsResourcesFiltersSelectors.getDateCreated); 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 402bdd514..336a96c32 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 @@ -12,8 +12,8 @@ import { PreprintProviderHeroComponent } from '@osf/features/preprints/component import { GetHighlightedSubjectsByProviderId, GetPreprintProviderById, - PreprintsSelectors, -} from '@osf/features/preprints/store/preprints'; + PreprintProvidersSelectors, +} from '@osf/features/preprints/store/preprint-providers'; import { BrandService } from '@shared/services'; import { BrowserTabHelper, HeaderStyleHelper } from '@shared/utils'; @@ -38,11 +38,11 @@ export class PreprintProviderOverviewComponent implements OnInit, OnDestroy { getPreprintProviderById: GetPreprintProviderById, getHighlightedSubjectsByProviderId: GetHighlightedSubjectsByProviderId, }); - preprintProvider = select(PreprintsSelectors.getPreprintProviderDetails(this.providerId())); - isPreprintProviderLoading = select(PreprintsSelectors.isPreprintProviderDetailsLoading); + preprintProvider = select(PreprintProvidersSelectors.getPreprintProviderDetails(this.providerId())); + isPreprintProviderLoading = select(PreprintProvidersSelectors.isPreprintProviderDetailsLoading); - highlightedSubjectsByProviderId = select(PreprintsSelectors.getHighlightedSubjectsForProvider); - areSubjectsLoading = select(PreprintsSelectors.areSubjectsLoading); + highlightedSubjectsByProviderId = select(PreprintProvidersSelectors.getHighlightedSubjectsForProvider); + areSubjectsLoading = select(PreprintProvidersSelectors.areSubjectsLoading); constructor() { effect(() => { diff --git a/src/app/features/preprints/pages/select-preprint-service/select-preprint-service.component.ts b/src/app/features/preprints/pages/select-preprint-service/select-preprint-service.component.ts index 7ca811f7f..453415ee4 100644 --- a/src/app/features/preprints/pages/select-preprint-service/select-preprint-service.component.ts +++ b/src/app/features/preprints/pages/select-preprint-service/select-preprint-service.component.ts @@ -12,7 +12,10 @@ import { ChangeDetectionStrategy, Component, HostBinding, OnInit } from '@angula import { RouterLink } from '@angular/router'; import { PreprintProviderShortInfo } from '@osf/features/preprints/models'; -import { GetPreprintProvidersAllowingSubmissions, PreprintsSelectors } from '@osf/features/preprints/store/preprints'; +import { + GetPreprintProvidersAllowingSubmissions, + PreprintProvidersSelectors, +} from '@osf/features/preprints/store/preprint-providers'; import { SetSelectedPreprintProviderId, SubmitPreprintSelectors } from '@osf/features/preprints/store/submit-preprint'; import { SubHeaderComponent } from '@shared/components'; import { DecodeHtmlPipe } from '@shared/pipes'; @@ -32,8 +35,8 @@ export class SelectPreprintServiceComponent implements OnInit { setSelectedPreprintProviderId: SetSelectedPreprintProviderId, }); - preprintProvidersAllowingSubmissions = select(PreprintsSelectors.getPreprintProvidersAllowingSubmissions); - areProvidersLoading = select(PreprintsSelectors.arePreprintProvidersAllowingSubmissionsLoading); + preprintProvidersAllowingSubmissions = select(PreprintProvidersSelectors.getPreprintProvidersAllowingSubmissions); + areProvidersLoading = select(PreprintProvidersSelectors.arePreprintProvidersAllowingSubmissionsLoading); selectedProviderId = select(SubmitPreprintSelectors.getSelectedProviderId); skeletonArray = Array.from({ length: 8 }, (_, i) => i + 1); diff --git a/src/app/features/preprints/pages/submit-preprint-stepper/submit-preprint-stepper.component.ts b/src/app/features/preprints/pages/submit-preprint-stepper/submit-preprint-stepper.component.ts index fca7477e8..7c8322e52 100644 --- a/src/app/features/preprints/pages/submit-preprint-stepper/submit-preprint-stepper.component.ts +++ b/src/app/features/preprints/pages/submit-preprint-stepper/submit-preprint-stepper.component.ts @@ -24,7 +24,7 @@ import { } from '@osf/features/preprints/components'; import { submitPreprintSteps } from '@osf/features/preprints/constants'; import { SubmitSteps } from '@osf/features/preprints/enums'; -import { GetPreprintProviderById, PreprintsSelectors } from '@osf/features/preprints/store/preprints'; +import { GetPreprintProviderById, PreprintProvidersSelectors } from '@osf/features/preprints/store/preprint-providers'; import { ResetStateAndDeletePreprint, SetSelectedPreprintProviderId, @@ -56,8 +56,8 @@ export class SubmitPreprintStepperComponent implements OnInit, OnDestroy { readonly SubmitStepsEnum = SubmitSteps; readonly submitPreprintSteps = submitPreprintSteps; - preprintProvider = select(PreprintsSelectors.getPreprintProviderDetails(this.providerId())); - isPreprintProviderLoading = select(PreprintsSelectors.isPreprintProviderDetailsLoading); + preprintProvider = select(PreprintProvidersSelectors.getPreprintProviderDetails(this.providerId())); + isPreprintProviderLoading = select(PreprintProvidersSelectors.isPreprintProviderDetailsLoading); currentStep = signal(0); isWeb = toSignal(inject(IS_WEB)); diff --git a/src/app/features/preprints/store/preprint-providers/index.ts b/src/app/features/preprints/store/preprint-providers/index.ts new file mode 100644 index 000000000..73ee4f8ed --- /dev/null +++ b/src/app/features/preprints/store/preprint-providers/index.ts @@ -0,0 +1,4 @@ +export * from './preprint-providers.actions'; +export * from './preprint-providers.model'; +export * from './preprint-providers.selectors'; +export * from './preprint-providers.state'; diff --git a/src/app/features/preprints/store/preprints/preprints.actions.ts b/src/app/features/preprints/store/preprint-providers/preprint-providers.actions.ts similarity index 100% rename from src/app/features/preprints/store/preprints/preprints.actions.ts rename to src/app/features/preprints/store/preprint-providers/preprint-providers.actions.ts diff --git a/src/app/features/preprints/store/preprints/preprints.model.ts b/src/app/features/preprints/store/preprint-providers/preprint-providers.model.ts similarity index 90% rename from src/app/features/preprints/store/preprints/preprints.model.ts rename to src/app/features/preprints/store/preprint-providers/preprint-providers.model.ts index d9bc5680d..205e7ee9e 100644 --- a/src/app/features/preprints/store/preprints/preprints.model.ts +++ b/src/app/features/preprints/store/preprint-providers/preprint-providers.model.ts @@ -1,7 +1,7 @@ import { PreprintProviderDetails, PreprintProviderShortInfo, Subject } from '@osf/features/preprints/models'; import { AsyncStateModel } from '@shared/models'; -export interface PreprintsStateModel { +export interface PreprintProvidersStateModel { preprintProvidersDetails: AsyncStateModel; preprintProvidersToAdvertise: AsyncStateModel; preprintProvidersAllowingSubmissions: AsyncStateModel; diff --git a/src/app/features/preprints/store/preprint-providers/preprint-providers.selectors.ts b/src/app/features/preprints/store/preprint-providers/preprint-providers.selectors.ts new file mode 100644 index 000000000..55918d6b9 --- /dev/null +++ b/src/app/features/preprints/store/preprint-providers/preprint-providers.selectors.ts @@ -0,0 +1,41 @@ +import { Selector } from '@ngxs/store'; + +import { PreprintProvidersState, PreprintProvidersStateModel } from '@osf/features/preprints/store/preprint-providers'; + +export class PreprintProvidersSelectors { + @Selector([PreprintProvidersState]) + static getPreprintProviderDetails(providerId: string) { + return (state: { preprintProviders: PreprintProvidersStateModel }) => + state.preprintProviders.preprintProvidersDetails.data.find((provider) => provider.id.includes(providerId)); + } + + @Selector([PreprintProvidersState]) + static isPreprintProviderDetailsLoading(state: PreprintProvidersStateModel) { + return state.preprintProvidersDetails.isLoading; + } + + @Selector([PreprintProvidersState]) + static getPreprintProvidersToAdvertise(state: PreprintProvidersStateModel) { + return state.preprintProvidersToAdvertise.data; + } + + @Selector([PreprintProvidersState]) + static getPreprintProvidersAllowingSubmissions(state: PreprintProvidersStateModel) { + return state.preprintProvidersAllowingSubmissions.data; + } + + @Selector([PreprintProvidersState]) + static arePreprintProvidersAllowingSubmissionsLoading(state: PreprintProvidersStateModel) { + return state.preprintProvidersAllowingSubmissions.isLoading; + } + + @Selector([PreprintProvidersState]) + static getHighlightedSubjectsForProvider(state: PreprintProvidersStateModel) { + return state.highlightedSubjectsForProvider.data; + } + + @Selector([PreprintProvidersState]) + static areSubjectsLoading(state: PreprintProvidersStateModel) { + return state.highlightedSubjectsForProvider.isLoading; + } +} diff --git a/src/app/features/preprints/store/preprints/preprints.state.ts b/src/app/features/preprints/store/preprint-providers/preprint-providers.state.ts similarity index 87% rename from src/app/features/preprints/store/preprints/preprints.state.ts rename to src/app/features/preprints/store/preprint-providers/preprint-providers.state.ts index d7a0b237a..670efc00d 100644 --- a/src/app/features/preprints/store/preprints/preprints.state.ts +++ b/src/app/features/preprints/store/preprint-providers/preprint-providers.state.ts @@ -7,17 +7,17 @@ import { catchError } from 'rxjs/operators'; import { inject, Injectable } from '@angular/core'; import { PreprintsService } from '@osf/features/preprints/services'; + import { GetHighlightedSubjectsByProviderId, GetPreprintProviderById, GetPreprintProvidersAllowingSubmissions, GetPreprintProvidersToAdvertise, -} from '@osf/features/preprints/store/preprints/preprints.actions'; - -import { PreprintsStateModel } from './'; +} from './preprint-providers.actions'; +import { PreprintProvidersStateModel } from './preprint-providers.model'; -@State({ - name: 'preprints', +@State({ + name: 'preprintProviders', defaults: { preprintProvidersDetails: { data: [], @@ -42,12 +42,12 @@ import { PreprintsStateModel } from './'; }, }) @Injectable() -export class PreprintsState { +export class PreprintProvidersState { #preprintsService = inject(PreprintsService); private readonly REFRESH_INTERVAL = 5 * 60 * 1000; @Action(GetPreprintProviderById) - getPreprintProviderById(ctx: StateContext, action: GetPreprintProviderById) { + getPreprintProviderById(ctx: StateContext, action: GetPreprintProviderById) { const state = ctx.getState(); const cachedData = state.preprintProvidersDetails.data.find((p) => p.id === action.id); const shouldRefresh = this.shouldRefresh(cachedData?.lastFetched); @@ -79,7 +79,7 @@ export class PreprintsState { } @Action(GetPreprintProvidersToAdvertise) - getPreprintProvidersToAdvertise(ctx: StateContext) { + getPreprintProvidersToAdvertise(ctx: StateContext) { ctx.setState(patch({ preprintProvidersToAdvertise: patch({ isLoading: true }) })); return this.#preprintsService.getPreprintProvidersToAdvertise().pipe( @@ -98,7 +98,7 @@ export class PreprintsState { } @Action(GetPreprintProvidersAllowingSubmissions) - getPreprintProvidersAllowingSubmissions(ctx: StateContext) { + getPreprintProvidersAllowingSubmissions(ctx: StateContext) { ctx.setState(patch({ preprintProvidersAllowingSubmissions: patch({ isLoading: true }) })); return this.#preprintsService.getPreprintProvidersAllowingSubmissions().pipe( @@ -118,7 +118,7 @@ export class PreprintsState { @Action(GetHighlightedSubjectsByProviderId) getHighlightedSubjectsByProviderId( - ctx: StateContext, + ctx: StateContext, action: GetHighlightedSubjectsByProviderId ) { ctx.setState(patch({ highlightedSubjectsForProvider: patch({ isLoading: true }) })); @@ -146,7 +146,11 @@ export class PreprintsState { return Date.now() - lastFetched > this.REFRESH_INTERVAL; } - private handleError(ctx: StateContext, section: keyof PreprintsStateModel, error: Error) { + private handleError( + ctx: StateContext, + section: keyof PreprintProvidersStateModel, + error: Error + ) { ctx.patchState({ [section]: { ...ctx.getState()[section], diff --git a/src/app/features/preprints/store/preprints/index.ts b/src/app/features/preprints/store/preprints/index.ts deleted file mode 100644 index d1f1d4f1a..000000000 --- a/src/app/features/preprints/store/preprints/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './preprints.actions'; -export * from './preprints.model'; -export * from './preprints.selectors'; -export * from './preprints.state'; diff --git a/src/app/features/preprints/store/preprints/preprints.selectors.ts b/src/app/features/preprints/store/preprints/preprints.selectors.ts deleted file mode 100644 index 7c3ac8de3..000000000 --- a/src/app/features/preprints/store/preprints/preprints.selectors.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Selector } from '@ngxs/store'; - -import { PreprintsState, PreprintsStateModel } from '@osf/features/preprints/store/preprints'; - -export class PreprintsSelectors { - @Selector([PreprintsState]) - static getPreprintProviderDetails(providerId: string) { - return (state: { preprints: PreprintsStateModel }) => - state.preprints.preprintProvidersDetails.data.find((provider) => provider.id.includes(providerId)); - } - - @Selector([PreprintsState]) - static isPreprintProviderDetailsLoading(state: PreprintsStateModel) { - return state.preprintProvidersDetails.isLoading; - } - - @Selector([PreprintsState]) - static getPreprintProvidersToAdvertise(state: PreprintsStateModel) { - return state.preprintProvidersToAdvertise.data; - } - - @Selector([PreprintsState]) - static getPreprintProvidersAllowingSubmissions(state: PreprintsStateModel) { - return state.preprintProvidersAllowingSubmissions.data; - } - - @Selector([PreprintsState]) - static arePreprintProvidersAllowingSubmissionsLoading(state: PreprintsStateModel) { - return state.preprintProvidersAllowingSubmissions.isLoading; - } - - @Selector([PreprintsState]) - static getHighlightedSubjectsForProvider(state: PreprintsStateModel) { - return state.highlightedSubjectsForProvider.data; - } - - @Selector([PreprintsState]) - static areSubjectsLoading(state: PreprintsStateModel) { - return state.highlightedSubjectsForProvider.isLoading; - } -} From 187896ccde054e3cc4830ea61bcee0a603c78235 Mon Sep 17 00:00:00 2001 From: Roma Date: Thu, 3 Jul 2025 11:38:03 +0300 Subject: [PATCH 02/18] refactor(preprints-services): Renamed services to have 'preprints' prefix. Decomposed --- src/app/features/preprints/mappers/index.ts | 1 + .../mappers/preprint-providers.mapper.ts | 56 +++++++ .../preprints/mappers/preprints.mapper.ts | 58 +------- src/app/features/preprints/services/index.ts | 5 +- ...ce.ts => preprint-contributors.service.ts} | 2 +- .../services/preprint-files.service.ts | 96 ++++++++++++ ...ervice.ts => preprint-licenses.service.ts} | 2 +- .../services/preprint-providers.service.ts | 72 +++++++++ .../preprints/services/preprints.service.ts | 139 +----------------- .../preprint-providers.state.ts | 12 +- .../submit-preprint/submit-preprint.state.ts | 23 +-- 11 files changed, 255 insertions(+), 211 deletions(-) create mode 100644 src/app/features/preprints/mappers/preprint-providers.mapper.ts rename src/app/features/preprints/services/{contributors.service.ts => preprint-contributors.service.ts} (98%) create mode 100644 src/app/features/preprints/services/preprint-files.service.ts rename src/app/features/preprints/services/{licenses.service.ts => preprint-licenses.service.ts} (98%) create mode 100644 src/app/features/preprints/services/preprint-providers.service.ts diff --git a/src/app/features/preprints/mappers/index.ts b/src/app/features/preprints/mappers/index.ts index 7ed240bce..05b261f6b 100644 --- a/src/app/features/preprints/mappers/index.ts +++ b/src/app/features/preprints/mappers/index.ts @@ -1 +1,2 @@ +export * from './preprint-providers.mapper'; export * from './preprints.mapper'; diff --git a/src/app/features/preprints/mappers/preprint-providers.mapper.ts b/src/app/features/preprints/mappers/preprint-providers.mapper.ts new file mode 100644 index 000000000..3f046ce91 --- /dev/null +++ b/src/app/features/preprints/mappers/preprint-providers.mapper.ts @@ -0,0 +1,56 @@ +import { + PreprintProviderDetails, + PreprintProviderDetailsGetResponse, + PreprintProviderShortInfo, + Subject, + SubjectGetResponse, +} from '@osf/features/preprints/models'; + +export class PreprintProvidersMapper { + static fromPreprintProviderDetailsGetResponse(response: PreprintProviderDetailsGetResponse): PreprintProviderDetails { + const brandRaw = response.embeds!.brand.data; + return { + id: response.id, + name: response.attributes.name, + descriptionHtml: response.attributes.description, + advisoryBoardHtml: response.attributes.advisory_board, + examplePreprintId: response.attributes.example, + domain: response.attributes.domain, + footerLinksHtml: response.attributes.footer_links, + preprintWord: response.attributes.preprint_word, + allowSubmissions: response.attributes.allow_submissions, + brand: { + id: brandRaw.id, + name: brandRaw.attributes.name, + heroLogoImageUrl: brandRaw.attributes.hero_logo_image, + heroBackgroundImageUrl: brandRaw.attributes.hero_background_image, + topNavLogoImageUrl: brandRaw.attributes.topnav_logo_image, + primaryColor: brandRaw.attributes.primary_color, + secondaryColor: brandRaw.attributes.secondary_color, + }, + iri: response.links.iri, + faviconUrl: response.attributes.assets.favicon, + }; + } + + static toPreprintProviderShortInfoFromGetResponse( + response: PreprintProviderDetailsGetResponse[] + ): PreprintProviderShortInfo[] { + return response.map((item) => ({ + id: item.id, + descriptionHtml: item.attributes.description, + name: item.attributes.name, + whiteWideImageUrl: item.attributes.assets?.wide_white, + squareColorNoTransparentImageUrl: item.attributes.assets?.square_color_no_transparent, + })); + } + + static fromSubjectsGetResponse(providerId: string, response: SubjectGetResponse[]): Subject[] { + return response.map((subject) => ({ + id: subject.id, + text: subject.attributes.text, + taxonomy_name: subject.attributes.taxonomy_name, + preprintProviderId: providerId, + })); + } +} diff --git a/src/app/features/preprints/mappers/preprints.mapper.ts b/src/app/features/preprints/mappers/preprints.mapper.ts index 25e3a5cd1..84d3770af 100644 --- a/src/app/features/preprints/mappers/preprints.mapper.ts +++ b/src/app/features/preprints/mappers/preprints.mapper.ts @@ -1,63 +1,7 @@ import { ApiData } from '@core/models'; -import { - Preprint, - PreprintJsonApi, - PreprintProviderDetails, - PreprintProviderDetailsGetResponse, - PreprintProviderShortInfo, - PreprintsRelationshipsJsonApi, - Subject, - SubjectGetResponse, -} from '@osf/features/preprints/models'; +import { Preprint, PreprintJsonApi, PreprintsRelationshipsJsonApi } from '@osf/features/preprints/models'; export class PreprintsMapper { - static fromPreprintProviderDetailsGetResponse(response: PreprintProviderDetailsGetResponse): PreprintProviderDetails { - const brandRaw = response.embeds!.brand.data; - return { - id: response.id, - name: response.attributes.name, - descriptionHtml: response.attributes.description, - advisoryBoardHtml: response.attributes.advisory_board, - examplePreprintId: response.attributes.example, - domain: response.attributes.domain, - footerLinksHtml: response.attributes.footer_links, - preprintWord: response.attributes.preprint_word, - allowSubmissions: response.attributes.allow_submissions, - brand: { - id: brandRaw.id, - name: brandRaw.attributes.name, - heroLogoImageUrl: brandRaw.attributes.hero_logo_image, - heroBackgroundImageUrl: brandRaw.attributes.hero_background_image, - topNavLogoImageUrl: brandRaw.attributes.topnav_logo_image, - primaryColor: brandRaw.attributes.primary_color, - secondaryColor: brandRaw.attributes.secondary_color, - }, - iri: response.links.iri, - faviconUrl: response.attributes.assets.favicon, - }; - } - - static toPreprintProviderShortInfoFromGetResponse( - response: PreprintProviderDetailsGetResponse[] - ): PreprintProviderShortInfo[] { - return response.map((item) => ({ - id: item.id, - descriptionHtml: item.attributes.description, - name: item.attributes.name, - whiteWideImageUrl: item.attributes.assets?.wide_white, - squareColorNoTransparentImageUrl: item.attributes.assets?.square_color_no_transparent, - })); - } - - static fromSubjectsGetResponse(providerId: string, response: SubjectGetResponse[]): Subject[] { - return response.map((subject) => ({ - id: subject.id, - text: subject.attributes.text, - taxonomy_name: subject.attributes.taxonomy_name, - preprintProviderId: providerId, - })); - } - static toCreatePayload(title: string, abstract: string, providerId: string) { return { data: { diff --git a/src/app/features/preprints/services/index.ts b/src/app/features/preprints/services/index.ts index a88975c9e..339550d00 100644 --- a/src/app/features/preprints/services/index.ts +++ b/src/app/features/preprints/services/index.ts @@ -1,3 +1,6 @@ -export { ContributorsService } from './contributors.service'; +export { PreprintContributorsService } from './preprint-contributors.service'; +export { PreprintFilesService } from './preprint-files.service'; +export { PreprintLicensesService } from './preprint-licenses.service'; +export { PreprintProvidersService } from './preprint-providers.service'; export { PreprintsService } from './preprints.service'; export { PreprintsFiltersOptionsService } from './preprints-resource-filters.service'; diff --git a/src/app/features/preprints/services/contributors.service.ts b/src/app/features/preprints/services/preprint-contributors.service.ts similarity index 98% rename from src/app/features/preprints/services/contributors.service.ts rename to src/app/features/preprints/services/preprint-contributors.service.ts index 049393205..9fa26f3cd 100644 --- a/src/app/features/preprints/services/contributors.service.ts +++ b/src/app/features/preprints/services/preprint-contributors.service.ts @@ -13,7 +13,7 @@ import { environment } from 'src/environments/environment'; @Injectable({ providedIn: 'root', }) -export class ContributorsService { +export class PreprintContributorsService { private jsonApiService = inject(JsonApiService); private apiUrl = environment.apiUrl; diff --git a/src/app/features/preprints/services/preprint-files.service.ts b/src/app/features/preprints/services/preprint-files.service.ts new file mode 100644 index 000000000..141d255b8 --- /dev/null +++ b/src/app/features/preprints/services/preprint-files.service.ts @@ -0,0 +1,96 @@ +import { map, Observable, switchMap } from 'rxjs'; + +import { inject, Injectable } from '@angular/core'; + +import { Primitive, StringOrNull } from '@core/helpers'; +import { JsonApiService } from '@core/services'; +import { ApiData, JsonApiResponse } from '@osf/core/models'; +import { PreprintsMapper } from '@osf/features/preprints/mappers'; +import { + Preprint, + PreprintFilesLinks, + PreprintJsonApi, + PreprintsRelationshipsJsonApi, +} from '@osf/features/preprints/models'; +import { GetFileResponse, GetFilesResponse, IdName, NodeData, OsfFile } from '@osf/shared/models'; +import { FilesService } from '@shared/services'; + +import { environment } from 'src/environments/environment'; + +@Injectable({ + providedIn: 'root', +}) +export class PreprintFilesService { + private filesService = inject(FilesService); + private jsonApiService = inject(JsonApiService); + + updateFileRelationship(preprintId: string, fileId: string): Observable { + return this.jsonApiService + .patch>( + `${environment.apiUrl}/preprints/${preprintId}/`, + { + data: { + type: 'preprints', + id: preprintId, + attributes: {}, + relationships: { + primary_file: { + data: { + type: 'files', + id: fileId, + }, + }, + }, + }, + } + ) + .pipe(map((response) => PreprintsMapper.fromPreprintJsonApi(response))); + } + + getPreprintFilesLinks(id: string): Observable { + return this.jsonApiService.get(`${environment.apiUrl}/preprints/${id}/files/`).pipe( + map((response) => { + const rel = response.data[0].relationships; + const links = response.data[0].links; + + return { + filesLink: rel.files.links.related.href, + uploadFileLink: links.upload, + }; + }) + ); + } + + getAvailableProjects(searchTerm: StringOrNull): Observable { + const params: Record = {}; + params['page'] = 1; + if (searchTerm) { + params['filter[title]'] = searchTerm; + } + + return this.jsonApiService + .get>(`${environment.apiUrl}/users/me/nodes/`, params) + .pipe( + map((response) => { + return response.data.map((item) => ({ + id: item.id, + name: item.attributes.title, + })); + }) + ); + } + + getProjectFiles(projectId: string): Observable { + return this.jsonApiService.get(`${environment.apiUrl}/nodes/${projectId}/files/`).pipe( + switchMap((response: GetFilesResponse) => { + return this.jsonApiService + .get>(response.data[0].relationships.root_folder.links.related.href) + .pipe( + switchMap((fileResponse) => + this.filesService.getFilesWithoutFiltering(fileResponse.data.relationships.files.links.related.href) + ) + ); + }) + ); + } +} diff --git a/src/app/features/preprints/services/licenses.service.ts b/src/app/features/preprints/services/preprint-licenses.service.ts similarity index 98% rename from src/app/features/preprints/services/licenses.service.ts rename to src/app/features/preprints/services/preprint-licenses.service.ts index 06b62098c..b0438b7f5 100644 --- a/src/app/features/preprints/services/licenses.service.ts +++ b/src/app/features/preprints/services/preprint-licenses.service.ts @@ -18,7 +18,7 @@ import { environment } from 'src/environments/environment'; @Injectable({ providedIn: 'root', }) -export class LicensesService { +export class PreprintLicensesService { private apiUrl = environment.apiUrl; private readonly jsonApiService = inject(JsonApiService); diff --git a/src/app/features/preprints/services/preprint-providers.service.ts b/src/app/features/preprints/services/preprint-providers.service.ts new file mode 100644 index 000000000..d6342885d --- /dev/null +++ b/src/app/features/preprints/services/preprint-providers.service.ts @@ -0,0 +1,72 @@ +import { map, Observable } from 'rxjs'; + +import { inject, Injectable } from '@angular/core'; + +import { JsonApiResponse } from '@core/models'; +import { JsonApiService } from '@core/services'; +import { PreprintProvidersMapper } from '@osf/features/preprints/mappers'; +import { + PreprintProviderDetails, + PreprintProviderDetailsGetResponse, + PreprintProviderShortInfo, + Subject, + SubjectGetResponse, +} from '@osf/features/preprints/models'; + +import { environment } from 'src/environments/environment'; + +@Injectable({ + providedIn: 'root', +}) +export class PreprintProvidersService { + private jsonApiService = inject(JsonApiService); + private baseUrl = `${environment.apiUrl}/providers/preprints/`; + + getPreprintProviderById(id: string): Observable { + return this.jsonApiService + .get>(`${this.baseUrl}${id}/?embed=brand`) + .pipe( + map((response) => { + return PreprintProvidersMapper.fromPreprintProviderDetailsGetResponse(response.data); + }) + ); + } + + getPreprintProvidersToAdvertise(): Observable { + return this.jsonApiService + .get< + JsonApiResponse + >(`${this.baseUrl}?filter[advertise_on_discover_page]=true&reload=true`) + .pipe( + map((response) => { + return PreprintProvidersMapper.toPreprintProviderShortInfoFromGetResponse( + response.data.filter((item) => !item.id.includes('osf')) + ); + }) + ); + } + + getPreprintProvidersAllowingSubmissions(): Observable { + return this.jsonApiService + .get< + JsonApiResponse + >(`${this.baseUrl}?filter[allow_submissions]=true`) + .pipe( + map((response) => { + return PreprintProvidersMapper.toPreprintProviderShortInfoFromGetResponse(response.data); + }) + ); + } + + getHighlightedSubjectsByProviderId(providerId: string): Observable { + return this.jsonApiService + .get< + JsonApiResponse + >(`${this.baseUrl}${providerId}/subjects/highlighted/?page[size]=20`) + .pipe( + map((response) => { + return PreprintProvidersMapper.fromSubjectsGetResponse(providerId, response.data); + }) + ); + } +} diff --git a/src/app/features/preprints/services/preprints.service.ts b/src/app/features/preprints/services/preprints.service.ts index adc0e74b4..9e0a30141 100644 --- a/src/app/features/preprints/services/preprints.service.ts +++ b/src/app/features/preprints/services/preprints.service.ts @@ -1,24 +1,11 @@ -import { map, Observable, switchMap } from 'rxjs'; +import { map, Observable } from 'rxjs'; import { inject, Injectable } from '@angular/core'; -import { Primitive, StringOrNull } from '@core/helpers'; import { JsonApiService } from '@core/services'; -import { ApiData, JsonApiResponse } from '@osf/core/models'; +import { ApiData } from '@osf/core/models'; import { PreprintsMapper } from '@osf/features/preprints/mappers'; -import { - Preprint, - PreprintFilesLinks, - PreprintJsonApi, - PreprintProviderDetails, - PreprintProviderDetailsGetResponse, - PreprintProviderShortInfo, - PreprintsRelationshipsJsonApi, - Subject, - SubjectGetResponse, -} from '@osf/features/preprints/models'; -import { GetFileResponse, GetFilesResponse, IdName, NodeData, OsfFile } from '@shared/models'; -import { FilesService } from '@shared/services'; +import { Preprint, PreprintJsonApi, PreprintsRelationshipsJsonApi } from '@osf/features/preprints/models'; import { environment } from 'src/environments/environment'; @@ -26,9 +13,7 @@ import { environment } from 'src/environments/environment'; providedIn: 'root', }) export class PreprintsService { - private filesService = inject(FilesService); private jsonApiService = inject(JsonApiService); - private baseUrl = `${environment.apiUrl}/providers/preprints/`; private domainToApiFieldMap: Record = { title: 'title', @@ -39,54 +24,6 @@ export class PreprintsService { tags: 'tags', }; - getPreprintProviderById(id: string): Observable { - return this.jsonApiService - .get>(`${this.baseUrl}${id}/?embed=brand`) - .pipe( - map((response) => { - return PreprintsMapper.fromPreprintProviderDetailsGetResponse(response.data); - }) - ); - } - - getPreprintProvidersToAdvertise(): Observable { - return this.jsonApiService - .get< - JsonApiResponse - >(`${this.baseUrl}?filter[advertise_on_discover_page]=true&reload=true`) - .pipe( - map((response) => { - return PreprintsMapper.toPreprintProviderShortInfoFromGetResponse( - response.data.filter((item) => !item.id.includes('osf')) - ); - }) - ); - } - - getPreprintProvidersAllowingSubmissions(): Observable { - return this.jsonApiService - .get< - JsonApiResponse - >(`${this.baseUrl}?filter[allow_submissions]=true`) - .pipe( - map((response) => { - return PreprintsMapper.toPreprintProviderShortInfoFromGetResponse(response.data); - }) - ); - } - - getHighlightedSubjectsByProviderId(providerId: string): Observable { - return this.jsonApiService - .get< - JsonApiResponse - >(`${this.baseUrl}${providerId}/subjects/highlighted/?page[size]=20`) - .pipe( - map((response) => { - return PreprintsMapper.fromSubjectsGetResponse(providerId, response.data); - }) - ); - } - createPreprint(title: string, abstract: string, providerId: string) { const payload = PreprintsMapper.toCreatePayload(title, abstract, providerId); return this.jsonApiService @@ -121,76 +58,6 @@ export class PreprintsService { .pipe(map((response) => PreprintsMapper.fromPreprintJsonApi(response))); } - updateFileRelationship(preprintId: string, fileId: string): Observable { - return this.jsonApiService - .patch>( - `${environment.apiUrl}/preprints/${preprintId}/`, - { - data: { - type: 'preprints', - id: preprintId, - attributes: {}, - relationships: { - primary_file: { - data: { - type: 'files', - id: fileId, - }, - }, - }, - }, - } - ) - .pipe(map((response) => PreprintsMapper.fromPreprintJsonApi(response))); - } - - getPreprintFilesLinks(id: string): Observable { - return this.jsonApiService.get(`${environment.apiUrl}/preprints/${id}/files/`).pipe( - map((response) => { - const rel = response.data[0].relationships; - const links = response.data[0].links; - - return { - filesLink: rel.files.links.related.href, - uploadFileLink: links.upload, - }; - }) - ); - } - - getAvailableProjects(searchTerm: StringOrNull): Observable { - const params: Record = {}; - params['page'] = 1; - if (searchTerm) { - params['filter[title]'] = searchTerm; - } - - return this.jsonApiService - .get>(`${environment.apiUrl}/users/me/nodes/`, params) - .pipe( - map((response) => { - return response.data.map((item) => ({ - id: item.id, - name: item.attributes.title, - })); - }) - ); - } - - getProjectFiles(projectId: string): Observable { - return this.jsonApiService.get(`${environment.apiUrl}/nodes/${projectId}/files/`).pipe( - switchMap((response: GetFilesResponse) => { - return this.jsonApiService - .get>(response.data[0].relationships.root_folder.links.related.href) - .pipe( - switchMap((fileResponse) => - this.filesService.getFilesWithoutFiltering(fileResponse.data.relationships.files.links.related.href) - ) - ); - }) - ); - } - private mapPreprintDomainToApiPayload(domainPayload: Partial): Partial { const apiPayload: Record = {}; Object.entries(domainPayload).forEach(([key, value]) => { diff --git a/src/app/features/preprints/store/preprint-providers/preprint-providers.state.ts b/src/app/features/preprints/store/preprint-providers/preprint-providers.state.ts index 670efc00d..95bb7c6b4 100644 --- a/src/app/features/preprints/store/preprint-providers/preprint-providers.state.ts +++ b/src/app/features/preprints/store/preprint-providers/preprint-providers.state.ts @@ -6,7 +6,7 @@ import { catchError } from 'rxjs/operators'; import { inject, Injectable } from '@angular/core'; -import { PreprintsService } from '@osf/features/preprints/services'; +import { PreprintProvidersService } from '@osf/features/preprints/services'; import { GetHighlightedSubjectsByProviderId, @@ -43,7 +43,7 @@ import { PreprintProvidersStateModel } from './preprint-providers.model'; }) @Injectable() export class PreprintProvidersState { - #preprintsService = inject(PreprintsService); + private preprintProvidersService = inject(PreprintProvidersService); private readonly REFRESH_INTERVAL = 5 * 60 * 1000; @Action(GetPreprintProviderById) @@ -58,7 +58,7 @@ export class PreprintProvidersState { ctx.setState(patch({ preprintProvidersDetails: patch({ isLoading: true }) })); - return this.#preprintsService.getPreprintProviderById(action.id).pipe( + return this.preprintProvidersService.getPreprintProviderById(action.id).pipe( tap((preprintProvider) => { const exists = state.preprintProvidersDetails.data.some((p) => p.id === preprintProvider.id); preprintProvider.lastFetched = Date.now(); @@ -82,7 +82,7 @@ export class PreprintProvidersState { getPreprintProvidersToAdvertise(ctx: StateContext) { ctx.setState(patch({ preprintProvidersToAdvertise: patch({ isLoading: true }) })); - return this.#preprintsService.getPreprintProvidersToAdvertise().pipe( + return this.preprintProvidersService.getPreprintProvidersToAdvertise().pipe( tap((data) => { ctx.setState( patch({ @@ -101,7 +101,7 @@ export class PreprintProvidersState { getPreprintProvidersAllowingSubmissions(ctx: StateContext) { ctx.setState(patch({ preprintProvidersAllowingSubmissions: patch({ isLoading: true }) })); - return this.#preprintsService.getPreprintProvidersAllowingSubmissions().pipe( + return this.preprintProvidersService.getPreprintProvidersAllowingSubmissions().pipe( tap((data) => { ctx.setState( patch({ @@ -123,7 +123,7 @@ export class PreprintProvidersState { ) { ctx.setState(patch({ highlightedSubjectsForProvider: patch({ isLoading: true }) })); - return this.#preprintsService.getHighlightedSubjectsByProviderId(action.providerId).pipe( + return this.preprintProvidersService.getHighlightedSubjectsByProviderId(action.providerId).pipe( tap((subjects) => { ctx.setState( patch({ diff --git a/src/app/features/preprints/store/submit-preprint/submit-preprint.state.ts b/src/app/features/preprints/store/submit-preprint/submit-preprint.state.ts index 10f98cf1c..4b34b0d07 100644 --- a/src/app/features/preprints/store/submit-preprint/submit-preprint.state.ts +++ b/src/app/features/preprints/store/submit-preprint/submit-preprint.state.ts @@ -9,8 +9,12 @@ import { inject, Injectable } from '@angular/core'; import { PreprintFileSource } from '@osf/features/preprints/enums'; import { Preprint } from '@osf/features/preprints/models'; -import { ContributorsService, PreprintsService } from '@osf/features/preprints/services'; -import { LicensesService } from '@osf/features/preprints/services/licenses.service'; +import { + PreprintContributorsService, + PreprintFilesService, + PreprintLicensesService, + PreprintsService, +} from '@osf/features/preprints/services'; import { OsfFile } from '@shared/models'; import { FilesService } from '@shared/services'; @@ -83,9 +87,10 @@ import { @Injectable() export class SubmitPreprintState { private preprintsService = inject(PreprintsService); + private preprintFilesService = inject(PreprintFilesService); private fileService = inject(FilesService); - private contributorsService = inject(ContributorsService); - private licensesService = inject(LicensesService); + private contributorsService = inject(PreprintContributorsService); + private licensesService = inject(PreprintLicensesService); @Action(SetSelectedPreprintProviderId) setSelectedPreprintProviderId(ctx: StateContext, action: SetSelectedPreprintProviderId) { @@ -126,7 +131,7 @@ export class SubmitPreprintState { } ctx.setState(patch({ preprintFilesLinks: patch({ isLoading: true }) })); - return this.preprintsService.getPreprintFilesLinks(state.createdPreprint.data.id).pipe( + return this.preprintFilesService.getPreprintFilesLinks(state.createdPreprint.data.id).pipe( tap((preprintStorage) => { ctx.setState(patch({ preprintFilesLinks: patch({ isLoading: false, data: preprintStorage }) })); }) @@ -149,7 +154,7 @@ export class SubmitPreprintState { const createdFileId = file.id.split('/')[1]; ctx.dispatch(new GetPreprintFiles()); - return this.preprintsService.updateFileRelationship(state.createdPreprint.data!.id, createdFileId).pipe( + return this.preprintFilesService.updateFileRelationship(state.createdPreprint.data!.id, createdFileId).pipe( tap((preprint: Preprint) => { ctx.patchState({ createdPreprint: { @@ -210,7 +215,7 @@ export class SubmitPreprintState { getAvailableProjects(ctx: StateContext, action: GetAvailableProjects) { ctx.setState(patch({ availableProjects: patch({ isLoading: true }) })); - return this.preprintsService.getAvailableProjects(action.searchTerm).pipe( + return this.preprintFilesService.getAvailableProjects(action.searchTerm).pipe( tap((projects) => { ctx.setState( patch({ @@ -229,7 +234,7 @@ export class SubmitPreprintState { getProjectFiles(ctx: StateContext, action: GetProjectFiles) { ctx.setState(patch({ projectFiles: patch({ isLoading: true }) })); - return this.preprintsService.getProjectFiles(action.projectId).pipe( + return this.preprintFilesService.getProjectFiles(action.projectId).pipe( tap((files: OsfFile[]) => { ctx.setState( patch({ @@ -346,7 +351,7 @@ export class SubmitPreprintState { const fileIdAfterCopy = file.id.split('/')[1]; - return this.preprintsService.updateFileRelationship(createdPreprintId, fileIdAfterCopy).pipe( + return this.preprintFilesService.updateFileRelationship(createdPreprintId, fileIdAfterCopy).pipe( tap((preprint: Preprint) => { ctx.patchState({ createdPreprint: { From 06650247ff12814e28668de5824c82ca7dd48777 Mon Sep 17 00:00:00 2001 From: Roma Date: Thu, 3 Jul 2025 11:41:32 +0300 Subject: [PATCH 03/18] fix(json-api-post): Fixed errors during to merge conflict --- src/app/features/preprints/services/preprints.service.ts | 6 +++--- src/app/shared/services/files.service.ts | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/app/features/preprints/services/preprints.service.ts b/src/app/features/preprints/services/preprints.service.ts index 9e0a30141..af95335f2 100644 --- a/src/app/features/preprints/services/preprints.service.ts +++ b/src/app/features/preprints/services/preprints.service.ts @@ -3,7 +3,7 @@ import { map, Observable } from 'rxjs'; import { inject, Injectable } from '@angular/core'; import { JsonApiService } from '@core/services'; -import { ApiData } from '@osf/core/models'; +import { ApiData, JsonApiResponse } from '@osf/core/models'; import { PreprintsMapper } from '@osf/features/preprints/mappers'; import { Preprint, PreprintJsonApi, PreprintsRelationshipsJsonApi } from '@osf/features/preprints/models'; @@ -28,11 +28,11 @@ export class PreprintsService { const payload = PreprintsMapper.toCreatePayload(title, abstract, providerId); return this.jsonApiService .post< - ApiData + JsonApiResponse, null> >(`${environment.apiUrl}/preprints/`, payload) .pipe( map((response) => { - return PreprintsMapper.fromPreprintJsonApi(response); + return PreprintsMapper.fromPreprintJsonApi(response.data); }) ); } diff --git a/src/app/shared/services/files.service.ts b/src/app/shared/services/files.service.ts index fc63a2990..570418965 100644 --- a/src/app/shared/services/files.service.ts +++ b/src/app/shared/services/files.service.ts @@ -260,7 +260,9 @@ export class FilesService { resource: resourceId, }; return this.#jsonApiService - .post>(moveLink, body) - .pipe(map((response) => MapFile(response))); + .post< + JsonApiResponse, null> + >(moveLink, body) + .pipe(map((response) => MapFile(response.data))); } } From 13e082d11908d024fbf8a5ec7acd672faeda6200 Mon Sep 17 00:00:00 2001 From: Roma Date: Thu, 3 Jul 2025 11:43:36 +0300 Subject: [PATCH 04/18] refactor(preprints-state): Renamed Preprints -> PreprintProviders --- .../preprint-providers/preprint-providers.actions.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/features/preprints/store/preprint-providers/preprint-providers.actions.ts b/src/app/features/preprints/store/preprint-providers/preprint-providers.actions.ts index 9254138a9..fce5ff94e 100644 --- a/src/app/features/preprints/store/preprint-providers/preprint-providers.actions.ts +++ b/src/app/features/preprints/store/preprint-providers/preprint-providers.actions.ts @@ -1,19 +1,19 @@ export class GetPreprintProviderById { - static readonly type = '[Preprints] Get Provider By Id'; + static readonly type = '[Preprint Providers] Get Provider By Id'; constructor(public id: string) {} } export class GetHighlightedSubjectsByProviderId { - static readonly type = '[Preprints] Get Highlighted Subjects By Provider Id'; + static readonly type = '[Preprint Providers] Get Highlighted Subjects By Provider Id'; constructor(public providerId: string) {} } export class GetPreprintProvidersToAdvertise { - static readonly type = '[Preprints] Get Preprint Providers To Advertise'; + static readonly type = '[Preprint Providers] Get Preprint Providers To Advertise'; } export class GetPreprintProvidersAllowingSubmissions { - static readonly type = '[Preprints] Get Preprint Providers That Allows Submissions'; + static readonly type = '[Preprint Providers] Get Preprint Providers That Allows Submissions'; } From a55bd9340406cb82cbe67668b38440f2390ebac0 Mon Sep 17 00:00:00 2001 From: Roma Date: Thu, 3 Jul 2025 12:21:23 +0300 Subject: [PATCH 05/18] refactor(preprints-models): Decomposed, used shared models. Moved some to shared --- .../browse-by-subjects.component.html | 2 +- .../browse-by-subjects.component.ts | 8 +- src/app/features/preprints/constants/index.ts | 1 - .../mappers/preprint-providers.mapper.ts | 18 +-- src/app/features/preprints/models/index.ts | 5 +- .../models/preprint-json-api.models.ts | 44 +++++ .../preprint-provider-json-api.models.ts | 30 ++++ .../models/preprint-provider.models.ts | 26 +++ .../preprints/models/preprint.models.ts | 35 ++++ .../preprints/models/preprints.models.ts | 150 ------------------ .../services/preprint-providers.service.ts | 19 +-- .../preprint-providers.model.ts | 4 +- src/app/shared/models/brand.json-api.model.ts | 12 ++ src/app/shared/models/index.ts | 1 + .../shared/models/subject/subject.model.ts | 1 + .../models/subject/subjects-json-api.model.ts | 6 +- 16 files changed, 179 insertions(+), 183 deletions(-) create mode 100644 src/app/features/preprints/models/preprint-json-api.models.ts create mode 100644 src/app/features/preprints/models/preprint-provider-json-api.models.ts create mode 100644 src/app/features/preprints/models/preprint-provider.models.ts create mode 100644 src/app/features/preprints/models/preprint.models.ts delete mode 100644 src/app/features/preprints/models/preprints.models.ts create mode 100644 src/app/shared/models/brand.json-api.model.ts diff --git a/src/app/features/preprints/components/browse-by-subjects/browse-by-subjects.component.html b/src/app/features/preprints/components/browse-by-subjects/browse-by-subjects.component.html index 2aff25b20..6ab5364ec 100644 --- a/src/app/features/preprints/components/browse-by-subjects/browse-by-subjects.component.html +++ b/src/app/features/preprints/components/browse-by-subjects/browse-by-subjects.component.html @@ -10,7 +10,7 @@

{{ 'preprints.browseBySubjects.title' | translate }}

({ id: item.id, @@ -45,12 +44,11 @@ export class PreprintProvidersMapper { })); } - static fromSubjectsGetResponse(providerId: string, response: SubjectGetResponse[]): Subject[] { - return response.map((subject) => ({ + static fromSubjectsGetResponse(data: SubjectDataJsonApi[]): Subject[] { + return data.map((subject) => ({ id: subject.id, - text: subject.attributes.text, - taxonomy_name: subject.attributes.taxonomy_name, - preprintProviderId: providerId, + name: subject.attributes.text, + iri: subject.links.iri, })); } } diff --git a/src/app/features/preprints/models/index.ts b/src/app/features/preprints/models/index.ts index 1cf98e527..8df6fdc96 100644 --- a/src/app/features/preprints/models/index.ts +++ b/src/app/features/preprints/models/index.ts @@ -1,3 +1,6 @@ +export * from './preprint.models'; +export * from './preprint-json-api.models'; export * from './preprint-licenses-json-api.models'; -export * from './preprints.models'; +export * from './preprint-provider.models'; +export * from './preprint-provider-json-api.models'; export * from './submit-preprint-form.models'; diff --git a/src/app/features/preprints/models/preprint-json-api.models.ts b/src/app/features/preprints/models/preprint-json-api.models.ts new file mode 100644 index 000000000..f826748ae --- /dev/null +++ b/src/app/features/preprints/models/preprint-json-api.models.ts @@ -0,0 +1,44 @@ +import { StringOrNull } from '@core/helpers'; +import { LicenseRecordJsonApi } from '@shared/models'; + +export interface PreprintJsonApi { + date_created: string; + date_modified: string; + date_published: Date | null; + original_publication_date: Date | null; + custom_publication_citation: StringOrNull; + doi: StringOrNull; + preprint_doi_created: Date | null; + title: string; + description: string; + is_published: boolean; + is_preprint_orphan: boolean; + license_record: LicenseRecordJsonApi | null; + tags: string[]; + date_withdrawn: Date | null; + current_user_permissions: string[]; + public: boolean; + reviews_state: string; + date_last_transitioned: Date | null; + version: number; + is_latest_version: boolean; + has_coi: boolean; + conflict_of_interest_statement: StringOrNull; + has_data_links: boolean; +} + +export interface PreprintsRelationshipsJsonApi { + primary_file: { + links: { + related: { + href: string; + }; + }; + }; + license: { + data: { + id: string; + type: 'licenses'; + }; + }; +} diff --git a/src/app/features/preprints/models/preprint-provider-json-api.models.ts b/src/app/features/preprints/models/preprint-provider-json-api.models.ts new file mode 100644 index 000000000..aee9d93fc --- /dev/null +++ b/src/app/features/preprints/models/preprint-provider-json-api.models.ts @@ -0,0 +1,30 @@ +import { StringOrNull } from '@core/helpers'; +import { BrandGetResponse } from '@shared/models'; + +export interface PreprintProviderDetailsJsonApi { + id: string; + type: 'preprint-providers'; + attributes: { + name: string; + description: string; + advisory_board: StringOrNull; + example: string; + domain: string; + footer_links: string; + preprint_word: string; + assets: { + wide_white: string; + square_color_no_transparent: string; + favicon: string; + }; + allow_submissions: boolean; + }; + embeds?: { + brand: { + data: BrandGetResponse; + }; + }; + links: { + iri: string; + }; +} diff --git a/src/app/features/preprints/models/preprint-provider.models.ts b/src/app/features/preprints/models/preprint-provider.models.ts new file mode 100644 index 000000000..8979fd539 --- /dev/null +++ b/src/app/features/preprints/models/preprint-provider.models.ts @@ -0,0 +1,26 @@ +import { StringOrNull } from '@core/helpers'; +import { Brand } from '@shared/models'; + +export interface PreprintProviderDetails { + id: string; + name: string; + descriptionHtml: string; + advisoryBoardHtml: StringOrNull; + examplePreprintId: string; + domain: string; + footerLinksHtml: string; + preprintWord: string; + allowSubmissions: boolean; + brand: Brand; + lastFetched?: number; + iri: string; + faviconUrl: string; +} + +export interface PreprintProviderShortInfo { + id: string; + name: string; + descriptionHtml: string; + whiteWideImageUrl: string; + squareColorNoTransparentImageUrl: string; +} diff --git a/src/app/features/preprints/models/preprint.models.ts b/src/app/features/preprints/models/preprint.models.ts new file mode 100644 index 000000000..be4f0e67c --- /dev/null +++ b/src/app/features/preprints/models/preprint.models.ts @@ -0,0 +1,35 @@ +import { StringOrNull } from '@core/helpers'; +import { LicenseOptions } from '@shared/models'; + +export interface Preprint { + id: string; + dateCreated: string; + dateModified: string; + title: string; + description: string; + doi: StringOrNull; + originalPublicationDate: Date | null; + customPublicationCitation: StringOrNull; + isPublished: boolean; + tags: string[]; + isPublic: boolean; + version: number; + isLatestVersion: boolean; + primaryFileId: StringOrNull; + licenseId: StringOrNull; + licenseOptions: LicenseOptions | null; +} + +export interface PreprintFilesLinks { + filesLink: string; + uploadFileLink: string; +} + +export interface SubjectGetResponse { + id: string; + type: 'subjects'; + attributes: { + text: string; + taxonomy_name: string; + }; +} diff --git a/src/app/features/preprints/models/preprints.models.ts b/src/app/features/preprints/models/preprints.models.ts deleted file mode 100644 index c1693a546..000000000 --- a/src/app/features/preprints/models/preprints.models.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { StringOrNull } from '@core/helpers'; -import { Brand } from '@osf/shared/models'; -import { LicenseOptions, LicenseRecordJsonApi } from '@shared/models'; - -export interface PreprintProviderDetails { - id: string; - name: string; - descriptionHtml: string; - advisoryBoardHtml: StringOrNull; - examplePreprintId: string; - domain: string; - footerLinksHtml: string; - preprintWord: string; - allowSubmissions: boolean; - brand: Brand; - lastFetched?: number; - iri: string; - faviconUrl: string; -} - -export interface PreprintProviderShortInfo { - id: string; - name: string; - descriptionHtml: string; - whiteWideImageUrl: string; - squareColorNoTransparentImageUrl: string; -} - -export interface Subject { - id: string; - text: string; - taxonomy_name: string; - preprintProviderId: string; -} - -export interface Preprint { - id: string; - dateCreated: string; - dateModified: string; - title: string; - description: string; - doi: StringOrNull; - originalPublicationDate: Date | null; - customPublicationCitation: StringOrNull; - isPublished: boolean; - tags: string[]; - isPublic: boolean; - version: number; - isLatestVersion: boolean; - primaryFileId: StringOrNull; - licenseId: StringOrNull; - licenseOptions: LicenseOptions | null; -} - -export interface PreprintFilesLinks { - filesLink: string; - uploadFileLink: string; -} - -export interface PreprintProviderDetailsGetResponse { - id: string; - type: 'preprint-providers'; - attributes: { - name: string; - description: string; - advisory_board: StringOrNull; - example: string; - domain: string; - footer_links: string; - preprint_word: string; - assets: { - wide_white: string; - square_color_no_transparent: string; - favicon: string; - }; - allow_submissions: boolean; - }; - embeds?: { - brand: { - data: BrandGetResponse; - }; - }; - links: { - iri: string; - }; -} - -export interface BrandGetResponse { - id: string; - type: 'brands'; - attributes: { - name: string; - hero_logo_image: string; - hero_background_image: string; - topnav_logo_image: string; - primary_color: string; - secondary_color: string; - }; -} - -export interface SubjectGetResponse { - id: string; - type: 'subjects'; - attributes: { - text: string; - taxonomy_name: string; - }; -} - -export interface PreprintJsonApi { - date_created: string; - date_modified: string; - date_published: Date | null; - original_publication_date: Date | null; - custom_publication_citation: StringOrNull; - doi: StringOrNull; - preprint_doi_created: Date | null; - title: string; - description: string; - is_published: boolean; - is_preprint_orphan: boolean; - license_record: LicenseRecordJsonApi | null; - tags: string[]; - date_withdrawn: Date | null; - current_user_permissions: string[]; - public: boolean; - reviews_state: string; - date_last_transitioned: Date | null; - version: number; - is_latest_version: boolean; - has_coi: boolean; - conflict_of_interest_statement: StringOrNull; - has_data_links: boolean; -} - -export interface PreprintsRelationshipsJsonApi { - primary_file: { - links: { - related: { - href: string; - }; - }; - }; - license: { - data: { - id: string; - type: 'licenses'; - }; - }; -} diff --git a/src/app/features/preprints/services/preprint-providers.service.ts b/src/app/features/preprints/services/preprint-providers.service.ts index d6342885d..044e5ddfb 100644 --- a/src/app/features/preprints/services/preprint-providers.service.ts +++ b/src/app/features/preprints/services/preprint-providers.service.ts @@ -7,11 +7,10 @@ import { JsonApiService } from '@core/services'; import { PreprintProvidersMapper } from '@osf/features/preprints/mappers'; import { PreprintProviderDetails, - PreprintProviderDetailsGetResponse, + PreprintProviderDetailsJsonApi, PreprintProviderShortInfo, - Subject, - SubjectGetResponse, } from '@osf/features/preprints/models'; +import { Subject, SubjectsResponseJsonApi } from '@shared/models'; import { environment } from 'src/environments/environment'; @@ -24,7 +23,7 @@ export class PreprintProvidersService { getPreprintProviderById(id: string): Observable { return this.jsonApiService - .get>(`${this.baseUrl}${id}/?embed=brand`) + .get>(`${this.baseUrl}${id}/?embed=brand`) .pipe( map((response) => { return PreprintProvidersMapper.fromPreprintProviderDetailsGetResponse(response.data); @@ -35,7 +34,7 @@ export class PreprintProvidersService { getPreprintProvidersToAdvertise(): Observable { return this.jsonApiService .get< - JsonApiResponse + JsonApiResponse >(`${this.baseUrl}?filter[advertise_on_discover_page]=true&reload=true`) .pipe( map((response) => { @@ -48,9 +47,7 @@ export class PreprintProvidersService { getPreprintProvidersAllowingSubmissions(): Observable { return this.jsonApiService - .get< - JsonApiResponse - >(`${this.baseUrl}?filter[allow_submissions]=true`) + .get>(`${this.baseUrl}?filter[allow_submissions]=true`) .pipe( map((response) => { return PreprintProvidersMapper.toPreprintProviderShortInfoFromGetResponse(response.data); @@ -60,12 +57,10 @@ export class PreprintProvidersService { getHighlightedSubjectsByProviderId(providerId: string): Observable { return this.jsonApiService - .get< - JsonApiResponse - >(`${this.baseUrl}${providerId}/subjects/highlighted/?page[size]=20`) + .get(`${this.baseUrl}${providerId}/subjects/highlighted/?page[size]=20`) .pipe( map((response) => { - return PreprintProvidersMapper.fromSubjectsGetResponse(providerId, response.data); + return PreprintProvidersMapper.fromSubjectsGetResponse(response.data); }) ); } diff --git a/src/app/features/preprints/store/preprint-providers/preprint-providers.model.ts b/src/app/features/preprints/store/preprint-providers/preprint-providers.model.ts index 205e7ee9e..b14ee13cb 100644 --- a/src/app/features/preprints/store/preprint-providers/preprint-providers.model.ts +++ b/src/app/features/preprints/store/preprint-providers/preprint-providers.model.ts @@ -1,5 +1,5 @@ -import { PreprintProviderDetails, PreprintProviderShortInfo, Subject } from '@osf/features/preprints/models'; -import { AsyncStateModel } from '@shared/models'; +import { PreprintProviderDetails, PreprintProviderShortInfo } from '@osf/features/preprints/models'; +import { AsyncStateModel, Subject } from '@shared/models'; export interface PreprintProvidersStateModel { preprintProvidersDetails: AsyncStateModel; diff --git a/src/app/shared/models/brand.json-api.model.ts b/src/app/shared/models/brand.json-api.model.ts new file mode 100644 index 000000000..9a59fc814 --- /dev/null +++ b/src/app/shared/models/brand.json-api.model.ts @@ -0,0 +1,12 @@ +export interface BrandGetResponse { + id: string; + type: 'brands'; + attributes: { + name: string; + hero_logo_image: string; + hero_background_image: string; + topnav_logo_image: string; + primary_color: string; + secondary_color: string; + }; +} diff --git a/src/app/shared/models/index.ts b/src/app/shared/models/index.ts index 5386e39a8..13275a443 100644 --- a/src/app/shared/models/index.ts +++ b/src/app/shared/models/index.ts @@ -1,4 +1,5 @@ export * from './addons'; +export * from './brand.json-api.model'; export * from './brand.model'; export * from './charts'; export * from './confirmation-options.model'; diff --git a/src/app/shared/models/subject/subject.model.ts b/src/app/shared/models/subject/subject.model.ts index 83e715e5e..78f50f91a 100644 --- a/src/app/shared/models/subject/subject.model.ts +++ b/src/app/shared/models/subject/subject.model.ts @@ -4,4 +4,5 @@ export interface Subject { children?: Subject[]; parent?: Subject | null; expanded?: boolean; + iri?: string; } diff --git a/src/app/shared/models/subject/subjects-json-api.model.ts b/src/app/shared/models/subject/subjects-json-api.model.ts index 81fd4f296..da42934b6 100644 --- a/src/app/shared/models/subject/subjects-json-api.model.ts +++ b/src/app/shared/models/subject/subjects-json-api.model.ts @@ -10,7 +10,7 @@ export type SubjectDataJsonApi = ApiData< SubjectAttributesJsonApi, SubjectEmbedsJsonApi, SubjectRelationshipsJsonApi, - null + SubjectLinksJsonApi >; interface SubjectAttributesJsonApi { @@ -18,6 +18,10 @@ interface SubjectAttributesJsonApi { taxonomy_name: string; } +interface SubjectLinksJsonApi { + iri: string; +} + interface SubjectRelationshipsJsonApi { parent?: { links: { From cf8cd638c3226d0c80346231574b2705184e4e6e Mon Sep 17 00:00:00 2001 From: Roma Date: Thu, 3 Jul 2025 12:21:56 +0300 Subject: [PATCH 06/18] refactor(router): Moved preprint routes to root folder of feature --- src/app/app.routes.ts | 3 +-- src/app/features/preprints/{constants => }/preprints.routes.ts | 0 2 files changed, 1 insertion(+), 2 deletions(-) rename src/app/features/preprints/{constants => }/preprints.routes.ts (100%) diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 95e085def..0899c6cf8 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -115,8 +115,7 @@ export const routes: Routes = [ }, { path: 'preprints', - loadChildren: () => - import('./features/preprints/constants/preprints.routes').then((mod) => mod.preprintsRoutes), + loadChildren: () => import('./features/preprints/preprints.routes').then((mod) => mod.preprintsRoutes), }, { path: 'search', diff --git a/src/app/features/preprints/constants/preprints.routes.ts b/src/app/features/preprints/preprints.routes.ts similarity index 100% rename from src/app/features/preprints/constants/preprints.routes.ts rename to src/app/features/preprints/preprints.routes.ts From c27d8da783862d3f159e50930390d94450093359 Mon Sep 17 00:00:00 2001 From: Roma Date: Thu, 3 Jul 2025 13:04:53 +0300 Subject: [PATCH 07/18] refactor(preprints-components): Rename and reorganize component files for consistency --- .../preprints-filter-chips.component.html | 0 .../preprints-filter-chips.component.scss | 0 .../preprints-filter-chips.component.spec.ts | 0 .../preprints-filter-chips.component.ts | 2 +- ...preprints-resources-filters.component.html | 0 ...preprints-resources-filters.component.scss | 0 ...prints-resources-filters.component.spec.ts | 2 +- .../preprints-resources-filters.component.ts | 13 ++++----- .../preprints-resources.component.html | 4 +-- .../preprints-resources.component.scss | 0 .../preprints-resources.component.spec.ts | 0 .../preprints-resources.component.ts | 0 .../preprints-subject-filter.component.html | 0 .../preprints-subject-filter.component.scss | 0 ...preprints-subject-filter.component.spec.ts | 0 .../preprints-subject-filter.component.ts | 0 .../features/preprints/components/index.ts | 28 ++++++++++--------- .../preprint-provider-hero.component.ts | 3 +- .../file-step/file-step.component.html | 0 .../file-step/file-step.component.scss | 0 .../file-step/file-step.component.spec.ts | 0 .../file-step/file-step.component.ts | 0 .../contributors/contributors.component.html | 0 .../contributors/contributors.component.scss | 0 .../contributors.component.spec.ts | 0 .../contributors/contributors.component.ts | 12 ++++---- .../metadata-step.component.html} | 0 .../metadata-step.component.scss} | 0 .../metadata-step.component.spec.ts} | 10 +++---- .../metadata-step/metadata-step.component.ts} | 6 ++-- .../title-and-abstract-step.component.html | 0 .../title-and-abstract-step.component.scss | 0 .../title-and-abstract-step.component.spec.ts | 0 .../title-and-abstract-step.component.ts | 0 .../preprint-provider-discover.component.ts | 5 ++-- .../preprint-provider-overview.component.ts | 9 ++++-- .../submit-preprint-stepper.component.ts | 6 ++-- 37 files changed, 52 insertions(+), 48 deletions(-) rename src/app/features/preprints/components/{ => filters}/preprints-filter-chips/preprints-filter-chips.component.html (100%) rename src/app/features/preprints/components/{ => filters}/preprints-filter-chips/preprints-filter-chips.component.scss (100%) rename src/app/features/preprints/components/{ => filters}/preprints-filter-chips/preprints-filter-chips.component.spec.ts (100%) rename src/app/features/preprints/components/{ => filters}/preprints-filter-chips/preprints-filter-chips.component.ts (97%) rename src/app/features/preprints/components/{ => filters}/preprints-resources-filters/preprints-resources-filters.component.html (100%) rename src/app/features/preprints/components/{ => filters}/preprints-resources-filters/preprints-resources-filters.component.scss (100%) rename src/app/features/preprints/components/{ => filters}/preprints-resources-filters/preprints-resources-filters.component.spec.ts (86%) rename src/app/features/preprints/components/{ => filters}/preprints-resources-filters/preprints-resources-filters.component.ts (78%) rename src/app/features/preprints/components/{ => filters}/preprints-resources/preprints-resources.component.html (96%) rename src/app/features/preprints/components/{ => filters}/preprints-resources/preprints-resources.component.scss (100%) rename src/app/features/preprints/components/{ => filters}/preprints-resources/preprints-resources.component.spec.ts (100%) rename src/app/features/preprints/components/{ => filters}/preprints-resources/preprints-resources.component.ts (100%) rename src/app/features/preprints/components/filters/{preprints-subject => preprints-subject-filter}/preprints-subject-filter.component.html (100%) rename src/app/features/preprints/components/filters/{preprints-subject => preprints-subject-filter}/preprints-subject-filter.component.scss (100%) rename src/app/features/preprints/components/filters/{preprints-subject => preprints-subject-filter}/preprints-subject-filter.component.spec.ts (100%) rename src/app/features/preprints/components/filters/{preprints-subject => preprints-subject-filter}/preprints-subject-filter.component.ts (100%) rename src/app/features/preprints/components/{submit-steps => stepper}/file-step/file-step.component.html (100%) rename src/app/features/preprints/components/{submit-steps => stepper}/file-step/file-step.component.scss (100%) rename src/app/features/preprints/components/{submit-steps => stepper}/file-step/file-step.component.spec.ts (100%) rename src/app/features/preprints/components/{submit-steps => stepper}/file-step/file-step.component.ts (100%) rename src/app/features/preprints/components/{submit-steps/metadata => stepper/metadata-step}/contributors/contributors.component.html (100%) rename src/app/features/preprints/components/{submit-steps/metadata => stepper/metadata-step}/contributors/contributors.component.scss (100%) rename src/app/features/preprints/components/{submit-steps/metadata => stepper/metadata-step}/contributors/contributors.component.spec.ts (100%) rename src/app/features/preprints/components/{submit-steps/metadata => stepper/metadata-step}/contributors/contributors.component.ts (94%) rename src/app/features/preprints/components/{submit-steps/metadata/metadata.component.html => stepper/metadata-step/metadata-step.component.html} (100%) rename src/app/features/preprints/components/{submit-steps/metadata/metadata.component.scss => stepper/metadata-step/metadata-step.component.scss} (100%) rename src/app/features/preprints/components/{submit-steps/metadata/metadata.component.spec.ts => stepper/metadata-step/metadata-step.component.spec.ts} (57%) rename src/app/features/preprints/components/{submit-steps/metadata/metadata.component.ts => stepper/metadata-step/metadata-step.component.ts} (96%) rename src/app/features/preprints/components/{submit-steps => stepper}/title-and-abstract-step/title-and-abstract-step.component.html (100%) rename src/app/features/preprints/components/{submit-steps => stepper}/title-and-abstract-step/title-and-abstract-step.component.scss (100%) rename src/app/features/preprints/components/{submit-steps => stepper}/title-and-abstract-step/title-and-abstract-step.component.spec.ts (100%) rename src/app/features/preprints/components/{submit-steps => stepper}/title-and-abstract-step/title-and-abstract-step.component.ts (100%) diff --git a/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.html b/src/app/features/preprints/components/filters/preprints-filter-chips/preprints-filter-chips.component.html similarity index 100% rename from src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.html rename to src/app/features/preprints/components/filters/preprints-filter-chips/preprints-filter-chips.component.html diff --git a/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.scss b/src/app/features/preprints/components/filters/preprints-filter-chips/preprints-filter-chips.component.scss similarity index 100% rename from src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.scss rename to src/app/features/preprints/components/filters/preprints-filter-chips/preprints-filter-chips.component.scss diff --git a/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.spec.ts b/src/app/features/preprints/components/filters/preprints-filter-chips/preprints-filter-chips.component.spec.ts similarity index 100% rename from src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.spec.ts rename to src/app/features/preprints/components/filters/preprints-filter-chips/preprints-filter-chips.component.spec.ts diff --git a/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.ts b/src/app/features/preprints/components/filters/preprints-filter-chips/preprints-filter-chips.component.ts similarity index 97% rename from src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.ts rename to src/app/features/preprints/components/filters/preprints-filter-chips/preprints-filter-chips.component.ts index a0a880991..82a2511eb 100644 --- a/src/app/features/preprints/components/preprints-filter-chips/preprints-filter-chips.component.ts +++ b/src/app/features/preprints/components/filters/preprints-filter-chips/preprints-filter-chips.component.ts @@ -13,7 +13,7 @@ import { SetSubject, } 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'; +import { FilterType } from '@shared/enums'; @Component({ selector: 'osf-preprints-filter-chips', diff --git a/src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.html b/src/app/features/preprints/components/filters/preprints-resources-filters/preprints-resources-filters.component.html similarity index 100% rename from src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.html rename to src/app/features/preprints/components/filters/preprints-resources-filters/preprints-resources-filters.component.html diff --git a/src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.scss b/src/app/features/preprints/components/filters/preprints-resources-filters/preprints-resources-filters.component.scss similarity index 100% rename from src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.scss rename to src/app/features/preprints/components/filters/preprints-resources-filters/preprints-resources-filters.component.scss diff --git a/src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.spec.ts b/src/app/features/preprints/components/filters/preprints-resources-filters/preprints-resources-filters.component.spec.ts similarity index 86% rename from src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.spec.ts rename to src/app/features/preprints/components/filters/preprints-resources-filters/preprints-resources-filters.component.spec.ts index 1f8c6791e..30480dcd0 100644 --- a/src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.spec.ts +++ b/src/app/features/preprints/components/filters/preprints-resources-filters/preprints-resources-filters.component.spec.ts @@ -1,6 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { PreprintsResourcesFiltersComponent } from './preprints-resources-filters.component'; +import { PreprintsResourcesFiltersComponent } from '@osf/features/preprints/components'; describe('PreprintsResourcesFiltersComponent', () => { let component: PreprintsResourcesFiltersComponent; diff --git a/src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.ts b/src/app/features/preprints/components/filters/preprints-resources-filters/preprints-resources-filters.component.ts similarity index 78% rename from src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.ts rename to src/app/features/preprints/components/filters/preprints-resources-filters/preprints-resources-filters.component.ts index 8b9f0ebe4..e1052ec1d 100644 --- a/src/app/features/preprints/components/preprints-resources-filters/preprints-resources-filters.component.ts +++ b/src/app/features/preprints/components/filters/preprints-resources-filters/preprints-resources-filters.component.ts @@ -4,15 +4,14 @@ import { Accordion, AccordionContent, AccordionHeader, AccordionPanel } from 'pr import { ChangeDetectionStrategy, Component, computed } from '@angular/core'; -import { - PreprintsCreatorsFilterComponent, - PreprintsDateCreatedFilterComponent, - PreprintsInstitutionFilterComponent, - PreprintsLicenseFilterComponent, - PreprintsSubjectFilterComponent, -} from '@osf/features/preprints/components'; import { PreprintsResourcesFiltersOptionsSelectors } from '@osf/features/preprints/store/preprints-resources-filters-options'; +import { PreprintsCreatorsFilterComponent } from '../preprints-creators-filter/preprints-creators-filter.component'; +import { PreprintsDateCreatedFilterComponent } from '../preprints-date-created-filter/preprints-date-created-filter.component'; +import { PreprintsInstitutionFilterComponent } from '../preprints-institution-filter/preprints-institution-filter.component'; +import { PreprintsLicenseFilterComponent } from '../preprints-license-filter/preprints-license-filter.component'; +import { PreprintsSubjectFilterComponent } from '../preprints-subject-filter/preprints-subject-filter.component'; + @Component({ selector: 'osf-preprints-resources-filters', imports: [ diff --git a/src/app/features/preprints/components/preprints-resources/preprints-resources.component.html b/src/app/features/preprints/components/filters/preprints-resources/preprints-resources.component.html similarity index 96% rename from src/app/features/preprints/components/preprints-resources/preprints-resources.component.html rename to src/app/features/preprints/components/filters/preprints-resources/preprints-resources.component.html index 568041e25..84ab7ee3a 100644 --- a/src/app/features/preprints/components/preprints-resources/preprints-resources.component.html +++ b/src/app/features/preprints/components/filters/preprints-resources/preprints-resources.component.html @@ -23,7 +23,7 @@

Sort by:

} @else { @if (isAnyFilterOptions()) { filter bySort by: /> } sort by { - let component: MetadataComponent; - let fixture: ComponentFixture; + let component: MetadataStepComponent; + let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [MetadataComponent], + imports: [MetadataStepComponent], }).compileComponents(); - fixture = TestBed.createComponent(MetadataComponent); + fixture = TestBed.createComponent(MetadataStepComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/src/app/features/preprints/components/submit-steps/metadata/metadata.component.ts b/src/app/features/preprints/components/stepper/metadata-step/metadata-step.component.ts similarity index 96% rename from src/app/features/preprints/components/submit-steps/metadata/metadata.component.ts rename to src/app/features/preprints/components/stepper/metadata-step/metadata-step.component.ts index c82bb1e9a..ac5a90e6e 100644 --- a/src/app/features/preprints/components/submit-steps/metadata/metadata.component.ts +++ b/src/app/features/preprints/components/stepper/metadata-step/metadata-step.component.ts @@ -45,11 +45,11 @@ import { ContributorsComponent } from './contributors/contributors.component'; LicenseComponent, TagsInputComponent, ], - templateUrl: './metadata.component.html', - styleUrl: './metadata.component.scss', + templateUrl: './metadata-step.component.html', + styleUrl: './metadata-step.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, }) -export class MetadataComponent implements OnInit { +export class MetadataStepComponent implements OnInit { private actions = createDispatchMap({ createPreprint: CreatePreprint, updatePreprint: UpdatePreprint, diff --git a/src/app/features/preprints/components/submit-steps/title-and-abstract-step/title-and-abstract-step.component.html b/src/app/features/preprints/components/stepper/title-and-abstract-step/title-and-abstract-step.component.html similarity index 100% rename from src/app/features/preprints/components/submit-steps/title-and-abstract-step/title-and-abstract-step.component.html rename to src/app/features/preprints/components/stepper/title-and-abstract-step/title-and-abstract-step.component.html diff --git a/src/app/features/preprints/components/submit-steps/title-and-abstract-step/title-and-abstract-step.component.scss b/src/app/features/preprints/components/stepper/title-and-abstract-step/title-and-abstract-step.component.scss similarity index 100% rename from src/app/features/preprints/components/submit-steps/title-and-abstract-step/title-and-abstract-step.component.scss rename to src/app/features/preprints/components/stepper/title-and-abstract-step/title-and-abstract-step.component.scss diff --git a/src/app/features/preprints/components/submit-steps/title-and-abstract-step/title-and-abstract-step.component.spec.ts b/src/app/features/preprints/components/stepper/title-and-abstract-step/title-and-abstract-step.component.spec.ts similarity index 100% rename from src/app/features/preprints/components/submit-steps/title-and-abstract-step/title-and-abstract-step.component.spec.ts rename to src/app/features/preprints/components/stepper/title-and-abstract-step/title-and-abstract-step.component.spec.ts diff --git a/src/app/features/preprints/components/submit-steps/title-and-abstract-step/title-and-abstract-step.component.ts b/src/app/features/preprints/components/stepper/title-and-abstract-step/title-and-abstract-step.component.ts similarity index 100% rename from src/app/features/preprints/components/submit-steps/title-and-abstract-step/title-and-abstract-step.component.ts rename to src/app/features/preprints/components/stepper/title-and-abstract-step/title-and-abstract-step.component.ts 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 a35e60a4d..c9775a57b 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 @@ -17,17 +17,16 @@ 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 { PreprintProviderHeroComponent, PreprintsResourcesComponent } from '@osf/features/preprints/components'; import { GetPreprintProviderById, PreprintProvidersSelectors } from '@osf/features/preprints/store/preprint-providers'; import { GetResources, + PreprintsDiscoverSelectors, 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, ResetFiltersState, 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 336a96c32..b18263877 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 @@ -6,9 +6,12 @@ import { ChangeDetectionStrategy, Component, effect, inject, OnDestroy, OnInit } import { toSignal } from '@angular/core/rxjs-interop'; import { ActivatedRoute, Router } from '@angular/router'; -import { AdvisoryBoardComponent, BrowseBySubjectsComponent } from '@osf/features/preprints/components'; -import { PreprintProviderFooterComponent } from '@osf/features/preprints/components/preprint-provider-footer/preprint-provider-footer.component'; -import { PreprintProviderHeroComponent } from '@osf/features/preprints/components/preprint-provider-hero/preprint-provider-hero.component'; +import { + AdvisoryBoardComponent, + BrowseBySubjectsComponent, + PreprintProviderFooterComponent, + PreprintProviderHeroComponent, +} from '@osf/features/preprints/components'; import { GetHighlightedSubjectsByProviderId, GetPreprintProviderById, diff --git a/src/app/features/preprints/pages/submit-preprint-stepper/submit-preprint-stepper.component.ts b/src/app/features/preprints/pages/submit-preprint-stepper/submit-preprint-stepper.component.ts index 7c8322e52..a9d946efb 100644 --- a/src/app/features/preprints/pages/submit-preprint-stepper/submit-preprint-stepper.component.ts +++ b/src/app/features/preprints/pages/submit-preprint-stepper/submit-preprint-stepper.component.ts @@ -19,7 +19,7 @@ import { ActivatedRoute } from '@angular/router'; import { FileStepComponent, - MetadataComponent, + MetadataStepComponent, TitleAndAbstractStepComponent, } from '@osf/features/preprints/components'; import { submitPreprintSteps } from '@osf/features/preprints/constants'; @@ -29,13 +29,13 @@ import { ResetStateAndDeletePreprint, SetSelectedPreprintProviderId, } from '@osf/features/preprints/store/submit-preprint'; -import { StepperComponent } from '@shared/components/stepper/stepper.component'; +import { StepperComponent } from '@shared/components'; import { BrandService } from '@shared/services'; import { BrowserTabHelper, HeaderStyleHelper, IS_WEB } from '@shared/utils'; @Component({ selector: 'osf-submit-preprint-stepper', - imports: [Skeleton, StepperComponent, TitleAndAbstractStepComponent, FileStepComponent, MetadataComponent], + imports: [Skeleton, StepperComponent, TitleAndAbstractStepComponent, FileStepComponent, MetadataStepComponent], templateUrl: './submit-preprint-stepper.component.html', styleUrl: './submit-preprint-stepper.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, From f6bfb320b0cc2ac0c7cffc6bd67f050e97fca0a0 Mon Sep 17 00:00:00 2001 From: Roma Date: Thu, 3 Jul 2025 13:16:57 +0300 Subject: [PATCH 08/18] fix(preprints-models): Removed unused model --- src/app/features/preprints/models/preprint.models.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/app/features/preprints/models/preprint.models.ts b/src/app/features/preprints/models/preprint.models.ts index be4f0e67c..4bc8a2be6 100644 --- a/src/app/features/preprints/models/preprint.models.ts +++ b/src/app/features/preprints/models/preprint.models.ts @@ -24,12 +24,3 @@ export interface PreprintFilesLinks { filesLink: string; uploadFileLink: string; } - -export interface SubjectGetResponse { - id: string; - type: 'subjects'; - attributes: { - text: string; - taxonomy_name: string; - }; -} From 189d6cb3e977fb4ae9d5d6e0ab2b230d11b68afa Mon Sep 17 00:00:00 2001 From: Roma Date: Thu, 3 Jul 2025 13:35:30 +0300 Subject: [PATCH 09/18] refactor(license): Made license more reusable by removing card wrapper --- .../metadata-step.component.html | 24 ++-- .../components/license/license.component.html | 115 ++++++++---------- .../components/license/license.component.ts | 2 - 3 files changed, 69 insertions(+), 72 deletions(-) diff --git a/src/app/features/preprints/components/stepper/metadata-step/metadata-step.component.html b/src/app/features/preprints/components/stepper/metadata-step/metadata-step.component.html index 682a46bb2..4ac5f5724 100644 --- a/src/app/features/preprints/components/stepper/metadata-step/metadata-step.component.html +++ b/src/app/features/preprints/components/stepper/metadata-step/metadata-step.component.html @@ -5,13 +5,23 @@

Metadata

- + +

{{ 'shared.license.title' | translate }}

+ +

{{ 'shared.license.description' | translate }}

+

+ {{ 'shared.license.helpText' | translate }} + {{ 'common.links.helpGuide' | translate }}. +

+ +
diff --git a/src/app/shared/components/license/license.component.html b/src/app/shared/components/license/license.component.html index da48a4ce0..585f9ca74 100644 --- a/src/app/shared/components/license/license.component.html +++ b/src/app/shared/components/license/license.component.html @@ -1,67 +1,56 @@ - -

{{ 'shared.license.title' | translate }}

- -

{{ 'shared.license.description' | translate }}

-

- {{ 'shared.license.helpText' | translate }} - {{ 'common.links.helpGuide' | translate }}. -

- - - @if (selectedLicense()) { - - @if (selectedLicense()!.requiredFields.length) { -
-
- - -
- +@if (selectedLicense()) { + + @if (selectedLicense()!.requiredFields.length) { + +
+ + - - } +
+ + + } -

- -

+

+ +

- @if (selectedLicense()!.requiredFields.length) { -
- - -
- } + @if (selectedLicense()!.requiredFields.length) { +
+ + +
} -
+} diff --git a/src/app/shared/components/license/license.component.ts b/src/app/shared/components/license/license.component.ts index 27eb3f58f..73ca61aef 100644 --- a/src/app/shared/components/license/license.component.ts +++ b/src/app/shared/components/license/license.component.ts @@ -1,7 +1,6 @@ import { TranslatePipe } from '@ngx-translate/core'; import { Button } from 'primeng/button'; -import { Card } from 'primeng/card'; import { DatePicker } from 'primeng/datepicker'; import { Divider } from 'primeng/divider'; import { Select } from 'primeng/select'; @@ -23,7 +22,6 @@ import { TruncatedTextComponent } from '../truncated-text/truncated-text.compone @Component({ selector: 'osf-license', imports: [ - Card, TranslatePipe, Select, FormsModule, From fe5e33eddef62eb2e41f27dd8323b42be8d76743 Mon Sep 17 00:00:00 2001 From: Roma Date: Thu, 3 Jul 2025 17:12:43 +0300 Subject: [PATCH 10/18] refactor(metadata-license): Removed redundant wrapper --- .../metadata-step.component.html | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/app/features/preprints/components/stepper/metadata-step/metadata-step.component.html b/src/app/features/preprints/components/stepper/metadata-step/metadata-step.component.html index 4ac5f5724..da7bc2379 100644 --- a/src/app/features/preprints/components/stepper/metadata-step/metadata-step.component.html +++ b/src/app/features/preprints/components/stepper/metadata-step/metadata-step.component.html @@ -4,25 +4,23 @@

Metadata

-
- -

{{ 'shared.license.title' | translate }}

+ +

{{ 'shared.license.title' | translate }}

-

{{ 'shared.license.description' | translate }}

-

- {{ 'shared.license.helpText' | translate }} - {{ 'common.links.helpGuide' | translate }}. -

- -
-
+

{{ 'shared.license.description' | translate }}

+

+ {{ 'shared.license.helpText' | translate }} + {{ 'common.links.helpGuide' | translate }}. +

+ +
From 04d15f2cd5af7adb3ebd90de2e60ae480d99ad7b Mon Sep 17 00:00:00 2001 From: Roma Date: Thu, 3 Jul 2025 18:23:33 +0300 Subject: [PATCH 11/18] fix(comments): Fixed comments --- .../preprints-resources/preprints-resources.component.html | 4 ++-- .../preprints/models/preprint-provider-json-api.models.ts | 4 ++-- src/app/shared/models/brand.json-api.model.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app/features/preprints/components/filters/preprints-resources/preprints-resources.component.html b/src/app/features/preprints/components/filters/preprints-resources/preprints-resources.component.html index 84ab7ee3a..568041e25 100644 --- a/src/app/features/preprints/components/filters/preprints-resources/preprints-resources.component.html +++ b/src/app/features/preprints/components/filters/preprints-resources/preprints-resources.component.html @@ -23,7 +23,7 @@

Sort by:

} @else { @if (isAnyFilterOptions()) { filter bySort by: /> } sort by Date: Thu, 3 Jul 2025 18:32:07 +0300 Subject: [PATCH 12/18] feat(author-assertions): Partly implemented step with missing available case handling --- .../author-assertions-step.component.html | 224 ++++++++++++++++++ .../author-assertions-step.component.scss | 8 + .../author-assertions-step.component.spec.ts | 22 ++ .../author-assertions-step.component.ts | 218 +++++++++++++++++ .../enums/applicability-status.enum.ts | 5 + src/app/features/preprints/enums/index.ts | 2 + .../preprints/enums/prereg-link-info.enum.ts | 5 + .../preprints/mappers/preprints.mapper.ts | 9 + .../models/preprint-json-api.models.ts | 9 +- .../preprints/models/preprint.models.ts | 10 + .../submit-preprint-stepper.component.html | 3 + .../submit-preprint-stepper.component.ts | 12 +- .../preprints/services/preprints.service.ts | 9 + 13 files changed, 533 insertions(+), 3 deletions(-) create mode 100644 src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.html create mode 100644 src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.scss create mode 100644 src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.spec.ts create mode 100644 src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.ts create mode 100644 src/app/features/preprints/enums/applicability-status.enum.ts create mode 100644 src/app/features/preprints/enums/prereg-link-info.enum.ts diff --git a/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.html b/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.html new file mode 100644 index 000000000..690da765a --- /dev/null +++ b/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.html @@ -0,0 +1,224 @@ +

Author Assertions

+ + +
+

Conflict of Interest

+ +

+ The Conflict of Interest (COI) assertion is made on behalf of all the authors listed for this preprint. COIs + include: financial involvement in any entity such as honoraria, grants, speaking fees, employment, consultancies, + stock ownership, expert testimony, and patents or licenses. COIs can also include non-financial interests such as + personal or professional relationships or pre-existing beliefs in the subject matter or materials discussed in + this preprint. +

+ +
+
+ + +
+ +
+ + +
+
+ + + @let coiStatementControl = authorAssertionsForm.controls['coiStatement']; + @if (coiStatementControl.errors?.['required'] && (coiStatementControl.touched || coiStatementControl.dirty)) { + + {{ INPUT_VALIDATION_MESSAGES.required | translate }} + + } +
+
+ + +
+

Public Data

+ +

+ Data refers to raw and/or processed information (quantitative or qualitative) used for the analyses, case studies, + and/or descriptive interpretation in the preprint. Public data could include data posted to open-access + repositories, public archival library collection, or government archive. For data that is available under limited + circumstances (e.g., after signing a data sharing agreement), choose the ‘No’ option and use the comment box to + explain how others could access the data. +

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+ + @let hasDataLinks = authorAssertionsForm.value.hasDataLinks!; + @if (hasDataLinks === ApplicabilityStatus.Unavailable || hasDataLinks === ApplicabilityStatus.NotApplicable) { + + @let whyNoDataControl = authorAssertionsForm.controls['whyNoData']; + @if (whyNoDataControl.errors?.['required'] && (whyNoDataControl.touched || whyNoDataControl.dirty)) { + + {{ INPUT_VALIDATION_MESSAGES.required | translate }} + + } + } @else { +

implement this list of adding links

+ } +
+
+ + +
+

Public Preregistration

+ +

+ A preregistration is a description of the research design and/or analysis plan that is created and registered + before researchers collected data or before they have seen/interacted with preexisting data. The description + should appear in a public registry (e.g., clinicaltrials.gov, OSF, AEA registry). +

+
+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+ @let hasPreregLinks = authorAssertionsForm.value.hasPreregLinks!; + @if (hasPreregLinks === ApplicabilityStatus.Unavailable || hasPreregLinks === ApplicabilityStatus.NotApplicable) { + + @let hasPreregLinksControl = authorAssertionsForm.controls['hasPreregLinks']; + @if (hasPreregLinksControl.errors?.['required'] && (hasPreregLinksControl.touched || hasPreregLinksControl.dirty)) { + + {{ INPUT_VALIDATION_MESSAGES.required | translate }} + + } + } @else { + + } +
+ +
+ + +
diff --git a/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.scss b/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.scss new file mode 100644 index 000000000..18b6612e7 --- /dev/null +++ b/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.scss @@ -0,0 +1,8 @@ +@use "assets/styles/mixins" as mix; +@use "assets/styles/variables" as var; + +.card { + @media (max-width: var.$breakpoint-sm) { + --p-card-body-padding: 0.75rem; + } +} diff --git a/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.spec.ts b/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.spec.ts new file mode 100644 index 000000000..1d71b74f8 --- /dev/null +++ b/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AuthorAssertionsStepComponent } from './author-assertions-step.component'; + +describe('AuthorAssertionsComponent', () => { + let component: AuthorAssertionsStepComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AuthorAssertionsStepComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(AuthorAssertionsStepComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.ts b/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.ts new file mode 100644 index 000000000..981ffc7fb --- /dev/null +++ b/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.ts @@ -0,0 +1,218 @@ +import { createDispatchMap, select } from '@ngxs/store'; + +import { TranslatePipe } from '@ngx-translate/core'; + +import { Button } from 'primeng/button'; +import { Card } from 'primeng/card'; +import { Message } from 'primeng/message'; +import { RadioButton } from 'primeng/radiobutton'; +import { Select } from 'primeng/select'; +import { Textarea } from 'primeng/textarea'; +import { Tooltip } from 'primeng/tooltip'; + +import { NgClass } from '@angular/common'; +import { ChangeDetectionStrategy, Component, effect, HostListener, inject, output } from '@angular/core'; +import { toSignal } from '@angular/core/rxjs-interop'; +import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; + +import { StringOrNull } from '@core/helpers'; +import { formInputLimits } from '@osf/features/preprints/constants'; +import { ApplicabilityStatus, PreregLinkInfo } from '@osf/features/preprints/enums'; +import { SubmitPreprintSelectors, UpdatePreprint } from '@osf/features/preprints/store/submit-preprint'; +import { INPUT_VALIDATION_MESSAGES } from '@shared/constants'; +import { ToastService } from '@shared/services'; + +@Component({ + selector: 'osf-author-assertions-step', + imports: [ + Card, + FormsModule, + RadioButton, + ReactiveFormsModule, + Textarea, + Message, + TranslatePipe, + NgClass, + Button, + Tooltip, + Select, + ], + templateUrl: './author-assertions-step.component.html', + styleUrl: './author-assertions-step.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AuthorAssertionsStepComponent { + private toastService = inject(ToastService); + private actions = createDispatchMap({ + updatePreprint: UpdatePreprint, + }); + + readonly ApplicabilityStatus = ApplicabilityStatus; + readonly inputLimits = formInputLimits; + readonly INPUT_VALIDATION_MESSAGES = INPUT_VALIDATION_MESSAGES; + readonly preregLinkOptions = Object.entries(PreregLinkInfo).map(([key, value]) => ({ + label: key, + value, + })); + + createdPreprint = select(SubmitPreprintSelectors.getCreatedPreprint); + isUpdatingPreprint = select(SubmitPreprintSelectors.isPreprintSubmitting); + + readonly authorAssertionsForm = new FormGroup({ + hasCoi: new FormControl(this.createdPreprint()!.hasCoi, { + nonNullable: true, + validators: [], + }), + coiStatement: new FormControl(this.createdPreprint()!.coiStatement, { + nonNullable: false, + validators: [], + }), + hasDataLinks: new FormControl(this.createdPreprint()!.hasDataLinks, { + nonNullable: true, + validators: [], + }), + whyNoData: new FormControl(this.createdPreprint()!.whyNoData, { + nonNullable: false, + validators: [], + }), + hasPreregLinks: new FormControl(this.createdPreprint()!.hasPreregLinks, { + nonNullable: true, + validators: [], + }), + whyNoPrereg: new FormControl(this.createdPreprint()!.whyNoPrereg, { + nonNullable: false, + validators: [], + }), + preregLinkInfo: new FormControl(this.createdPreprint()!.preregLinkInfo, { + nonNullable: false, + validators: [], + }), + }); + + hasCoiValue = toSignal(this.authorAssertionsForm.controls['hasCoi'].valueChanges, { + initialValue: this.createdPreprint()!.hasCoi, + }); + hasDataLinks = toSignal(this.authorAssertionsForm.controls['hasDataLinks'].valueChanges, { + initialValue: this.createdPreprint()!.hasDataLinks, + }); + hasPreregLinks = toSignal(this.authorAssertionsForm.controls['hasPreregLinks'].valueChanges, { + initialValue: this.createdPreprint()!.hasPreregLinks, + }); + + nextClicked = output(); + + constructor() { + effect(() => { + const hasCoi = this.hasCoiValue(); + const coiStatementControl = this.authorAssertionsForm.controls['coiStatement']; + + if (hasCoi) { + coiStatementControl.setValidators([Validators.required]); + coiStatementControl.enable(); + } else { + coiStatementControl.clearValidators(); + coiStatementControl.setValue(null); + coiStatementControl.disable(); + } + + coiStatementControl.updateValueAndValidity(); + }); + + effect(() => { + const hasDataLinks = this.hasDataLinks(); + const whyNoDataControl = this.authorAssertionsForm.controls['whyNoData']; + + switch (hasDataLinks) { + case ApplicabilityStatus.Unavailable: + whyNoDataControl.setValidators([Validators.required]); + whyNoDataControl.enable(); + break; + case ApplicabilityStatus.NotApplicable: + whyNoDataControl.clearValidators(); + whyNoDataControl.setValue(null); + whyNoDataControl.disable(); + break; + case ApplicabilityStatus.Applicable: + whyNoDataControl.clearValidators(); + whyNoDataControl.setValue(null); + whyNoDataControl.disable(); + break; + } + whyNoDataControl.updateValueAndValidity(); + }); + + effect(() => { + const hasDataLinks = this.hasPreregLinks(); + const whyNoPreregControl = this.authorAssertionsForm.controls['whyNoPrereg']; + const preregLinkInfoControl = this.authorAssertionsForm.controls['preregLinkInfo']; + + switch (hasDataLinks) { + case ApplicabilityStatus.Unavailable: + whyNoPreregControl.setValidators([Validators.required]); + whyNoPreregControl.enable(); + + preregLinkInfoControl.clearValidators(); + preregLinkInfoControl.setValue(null); + break; + case ApplicabilityStatus.NotApplicable: + whyNoPreregControl.clearValidators(); + whyNoPreregControl.setValue(null); + whyNoPreregControl.disable(); + + preregLinkInfoControl.clearValidators(); + preregLinkInfoControl.setValue(null); + break; + case ApplicabilityStatus.Applicable: + whyNoPreregControl.clearValidators(); + whyNoPreregControl.setValue(null); + whyNoPreregControl.disable(); + + preregLinkInfoControl.setValidators([Validators.required]); + break; + } + whyNoPreregControl.updateValueAndValidity(); + preregLinkInfoControl.updateValueAndValidity(); + }); + } + + @HostListener('window:beforeunload', ['$event']) + public onBeforeUnload($event: BeforeUnloadEvent): boolean { + $event.preventDefault(); + return false; + } + + nextButtonClicked() { + const formValue = this.authorAssertionsForm.value; + + const hasCoi = formValue.hasCoi; + const coiStatement = formValue.coiStatement || null; + + const hasDataLinks = formValue.hasDataLinks; + const whyNoData = formValue.whyNoData || null; + const dataLinks: string[] = []; + + const hasPreregLinks = formValue.hasPreregLinks; + const whyNoPrereg = formValue.whyNoPrereg || null; + const preregLinks: string[] = []; + const preregLinkInfo = formValue.preregLinkInfo; + + this.actions + .updatePreprint(this.createdPreprint()!.id, { + hasCoi, + coiStatement, + hasDataLinks, + whyNoData, + dataLinks, + hasPreregLinks, + whyNoPrereg, + preregLinks, + preregLinkInfo, + }) + .subscribe({ + complete: () => { + this.toastService.showSuccess('Preprint saved successfully.'); + this.nextClicked.emit(); + }, + }); + } +} diff --git a/src/app/features/preprints/enums/applicability-status.enum.ts b/src/app/features/preprints/enums/applicability-status.enum.ts new file mode 100644 index 000000000..ec14aaa5a --- /dev/null +++ b/src/app/features/preprints/enums/applicability-status.enum.ts @@ -0,0 +1,5 @@ +export enum ApplicabilityStatus { + Applicable = 'available', + NotApplicable = 'not_applicable', + Unavailable = 'no', +} diff --git a/src/app/features/preprints/enums/index.ts b/src/app/features/preprints/enums/index.ts index 5b8a8b7f9..bf1bf51c4 100644 --- a/src/app/features/preprints/enums/index.ts +++ b/src/app/features/preprints/enums/index.ts @@ -1,2 +1,4 @@ +export { ApplicabilityStatus } from './applicability-status.enum'; export { PreprintFileSource } from './preprint-file-source.enum'; +export { PreregLinkInfo } from './prereg-link-info.enum'; export { SubmitSteps } from './submit-steps.enum'; diff --git a/src/app/features/preprints/enums/prereg-link-info.enum.ts b/src/app/features/preprints/enums/prereg-link-info.enum.ts new file mode 100644 index 000000000..b96928fbf --- /dev/null +++ b/src/app/features/preprints/enums/prereg-link-info.enum.ts @@ -0,0 +1,5 @@ +export enum PreregLinkInfo { + Analysis = 'prereg_analysis', + Designs = 'prereg_designs', + Both = 'prereg_both', +} diff --git a/src/app/features/preprints/mappers/preprints.mapper.ts b/src/app/features/preprints/mappers/preprints.mapper.ts index 84d3770af..2661e1388 100644 --- a/src/app/features/preprints/mappers/preprints.mapper.ts +++ b/src/app/features/preprints/mappers/preprints.mapper.ts @@ -45,6 +45,15 @@ export class PreprintsMapper { copyrightHolders: response.attributes.license_record.copyright_holders.join(','), } : null, + hasCoi: response.attributes.has_coi, + coiStatement: response.attributes.conflict_of_interest_statement, + hasDataLinks: response.attributes.has_data_links, + dataLinks: response.attributes.data_links, + whyNoData: response.attributes.why_no_data, + hasPreregLinks: response.attributes.has_prereg_links, + whyNoPrereg: response.attributes.why_no_prereg, + preregLinks: response.attributes.prereg_links, + preregLinkInfo: response.attributes.prereg_link_info, }; } } diff --git a/src/app/features/preprints/models/preprint-json-api.models.ts b/src/app/features/preprints/models/preprint-json-api.models.ts index f826748ae..27877a0a5 100644 --- a/src/app/features/preprints/models/preprint-json-api.models.ts +++ b/src/app/features/preprints/models/preprint-json-api.models.ts @@ -1,4 +1,5 @@ import { StringOrNull } from '@core/helpers'; +import { ApplicabilityStatus, PreregLinkInfo } from '@osf/features/preprints/enums'; import { LicenseRecordJsonApi } from '@shared/models'; export interface PreprintJsonApi { @@ -24,7 +25,13 @@ export interface PreprintJsonApi { is_latest_version: boolean; has_coi: boolean; conflict_of_interest_statement: StringOrNull; - has_data_links: boolean; + has_data_links: ApplicabilityStatus; + data_links: string[]; + why_no_data: StringOrNull; + has_prereg_links: ApplicabilityStatus; + why_no_prereg: StringOrNull; + prereg_links: string[]; + prereg_link_info: PreregLinkInfo | null; } export interface PreprintsRelationshipsJsonApi { diff --git a/src/app/features/preprints/models/preprint.models.ts b/src/app/features/preprints/models/preprint.models.ts index 4bc8a2be6..bba170678 100644 --- a/src/app/features/preprints/models/preprint.models.ts +++ b/src/app/features/preprints/models/preprint.models.ts @@ -1,4 +1,5 @@ import { StringOrNull } from '@core/helpers'; +import { ApplicabilityStatus, PreregLinkInfo } from '@osf/features/preprints/enums'; import { LicenseOptions } from '@shared/models'; export interface Preprint { @@ -18,6 +19,15 @@ export interface Preprint { primaryFileId: StringOrNull; licenseId: StringOrNull; licenseOptions: LicenseOptions | null; + hasCoi: boolean; + coiStatement: StringOrNull; + hasDataLinks: ApplicabilityStatus; + dataLinks: string[]; + whyNoData: StringOrNull; + hasPreregLinks: ApplicabilityStatus; + whyNoPrereg: StringOrNull; + preregLinks: string[]; + preregLinkInfo: PreregLinkInfo | null; } export interface PreprintFilesLinks { diff --git a/src/app/features/preprints/pages/submit-preprint-stepper/submit-preprint-stepper.component.html b/src/app/features/preprints/pages/submit-preprint-stepper/submit-preprint-stepper.component.html index da30ac767..0e34f65fd 100644 --- a/src/app/features/preprints/pages/submit-preprint-stepper/submit-preprint-stepper.component.html +++ b/src/app/features/preprints/pages/submit-preprint-stepper/submit-preprint-stepper.component.html @@ -42,6 +42,9 @@

{{ 'Add a ' + preprintProvider()!.preprintWor @case (SubmitStepsEnum.Metadata) { } + @case (SubmitStepsEnum.AuthorAssertions) { + + } @default {

No such step

} diff --git a/src/app/features/preprints/pages/submit-preprint-stepper/submit-preprint-stepper.component.ts b/src/app/features/preprints/pages/submit-preprint-stepper/submit-preprint-stepper.component.ts index a9d946efb..9db2ec3e0 100644 --- a/src/app/features/preprints/pages/submit-preprint-stepper/submit-preprint-stepper.component.ts +++ b/src/app/features/preprints/pages/submit-preprint-stepper/submit-preprint-stepper.component.ts @@ -22,6 +22,7 @@ import { MetadataStepComponent, TitleAndAbstractStepComponent, } from '@osf/features/preprints/components'; +import { AuthorAssertionsStepComponent } from '@osf/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component'; import { submitPreprintSteps } from '@osf/features/preprints/constants'; import { SubmitSteps } from '@osf/features/preprints/enums'; import { GetPreprintProviderById, PreprintProvidersSelectors } from '@osf/features/preprints/store/preprint-providers'; @@ -35,7 +36,14 @@ import { BrowserTabHelper, HeaderStyleHelper, IS_WEB } from '@shared/utils'; @Component({ selector: 'osf-submit-preprint-stepper', - imports: [Skeleton, StepperComponent, TitleAndAbstractStepComponent, FileStepComponent, MetadataStepComponent], + imports: [ + Skeleton, + StepperComponent, + TitleAndAbstractStepComponent, + FileStepComponent, + MetadataStepComponent, + AuthorAssertionsStepComponent, + ], templateUrl: './submit-preprint-stepper.component.html', styleUrl: './submit-preprint-stepper.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, @@ -58,7 +66,7 @@ export class SubmitPreprintStepperComponent implements OnInit, OnDestroy { preprintProvider = select(PreprintProvidersSelectors.getPreprintProviderDetails(this.providerId())); isPreprintProviderLoading = select(PreprintProvidersSelectors.isPreprintProviderDetailsLoading); - currentStep = signal(0); + currentStep = signal(3); isWeb = toSignal(inject(IS_WEB)); constructor() { diff --git a/src/app/features/preprints/services/preprints.service.ts b/src/app/features/preprints/services/preprints.service.ts index af95335f2..0668a8060 100644 --- a/src/app/features/preprints/services/preprints.service.ts +++ b/src/app/features/preprints/services/preprints.service.ts @@ -22,6 +22,15 @@ export class PreprintsService { doi: 'doi', customPublicationCitation: 'custom_publication_citation', tags: 'tags', + hasCoi: 'has_coi', + coiStatement: 'conflict_of_interest_statement', + hasDataLinks: 'has_data_links', + dataLinks: 'data_links', + whyNoData: 'why_no_data', + hasPreregLinks: 'has_prereg_links', + preregLinks: 'prereg_links', + whyNoPrereg: 'why_no_prereg', + preregLinkInfo: 'prereg_link_info', }; createPreprint(title: string, abstract: string, providerId: string) { From 14051fe4d3d3530a6349a52c904c7c9c16a72c49 Mon Sep 17 00:00:00 2001 From: Roma Date: Thu, 3 Jul 2025 19:00:27 +0300 Subject: [PATCH 13/18] refactor(validators): Moved linkValidator from helper function to shared util --- src/app/core/helpers/index.ts | 1 - src/app/core/helpers/link-validator.helper.ts | 16 ---------------- .../developer-app-add-edit-form.component.ts | 7 +++---- .../utils/custom-form-validators.helper.ts | 15 +++++++++++++++ 4 files changed, 18 insertions(+), 21 deletions(-) delete mode 100644 src/app/core/helpers/link-validator.helper.ts diff --git a/src/app/core/helpers/index.ts b/src/app/core/helpers/index.ts index 71cd6afca..8a4ee14e8 100644 --- a/src/app/core/helpers/index.ts +++ b/src/app/core/helpers/index.ts @@ -1,4 +1,3 @@ export * from './http.helper'; export * from './i18n.helper'; -export * from './link-validator.helper'; export * from './types.helper'; diff --git a/src/app/core/helpers/link-validator.helper.ts b/src/app/core/helpers/link-validator.helper.ts deleted file mode 100644 index 03052cf5e..000000000 --- a/src/app/core/helpers/link-validator.helper.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms'; - -export function linkValidator(): ValidatorFn { - return (control: AbstractControl): ValidationErrors | null => { - const value = control.value; - if (!value) { - return null; - } - - const urlPattern = /^(https):\/\/.+/i; - - const isValid = urlPattern.test(value); - - return isValid ? null : { link: true }; - }; -} diff --git a/src/app/features/settings/developer-apps/components/developer-app-add-edit-form/developer-app-add-edit-form.component.ts b/src/app/features/settings/developer-apps/components/developer-app-add-edit-form/developer-app-add-edit-form.component.ts index 5c21d6e6b..7475ef369 100644 --- a/src/app/features/settings/developer-apps/components/developer-app-add-edit-form/developer-app-add-edit-form.component.ts +++ b/src/app/features/settings/developer-apps/components/developer-app-add-edit-form/developer-app-add-edit-form.component.ts @@ -12,8 +12,7 @@ import { toSignal } from '@angular/core/rxjs-interop'; import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; import { Router } from '@angular/router'; -import { linkValidator } from '@osf/core/helpers'; -import { IS_XSMALL } from '@osf/shared/utils'; +import { CustomValidators, IS_XSMALL } from '@osf/shared/utils'; import { DeveloperApp, DeveloperAppCreateUpdate, DeveloperAppForm, DeveloperAppFormFormControls } from '../../models'; import { CreateDeveloperApp, UpdateDeveloperApp } from '../../store'; @@ -41,14 +40,14 @@ export class DeveloperAppAddEditFormComponent implements OnInit { }), [DeveloperAppFormFormControls.ProjectHomePageUrl]: new FormControl('', { nonNullable: true, - validators: [Validators.required, linkValidator()], + validators: [Validators.required, CustomValidators.linkValidator()], }), [DeveloperAppFormFormControls.AppDescription]: new FormControl('', { nonNullable: false, }), [DeveloperAppFormFormControls.AuthorizationCallbackUrl]: new FormControl('', { nonNullable: true, - validators: [Validators.required, linkValidator()], + validators: [Validators.required, CustomValidators.linkValidator()], }), }); diff --git a/src/app/shared/utils/custom-form-validators.helper.ts b/src/app/shared/utils/custom-form-validators.helper.ts index 6e49ab71d..935966456 100644 --- a/src/app/shared/utils/custom-form-validators.helper.ts +++ b/src/app/shared/utils/custom-form-validators.helper.ts @@ -25,4 +25,19 @@ export class CustomValidators { return isValid ? null : { email: { value: control.value } }; }; } + + static linkValidator(): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const value = control.value; + if (!value) { + return null; + } + + const urlPattern = /^(https):\/\/.+/i; + + const isValid = urlPattern.test(value); + + return isValid ? null : { link: true }; + }; + } } From ddd89b233a72b76f175440bb9a746483a1015ea5 Mon Sep 17 00:00:00 2001 From: Roma Date: Thu, 3 Jul 2025 19:32:31 +0300 Subject: [PATCH 14/18] feat(array-input): Implemented reusable component for array input --- .../array-input/array-input.component.html | 17 ++++++++ .../array-input/array-input.component.scss | 0 .../array-input/array-input.component.spec.ts | 22 ++++++++++ .../array-input/array-input.component.ts | 41 +++++++++++++++++++ .../text-input/text-input.component.ts | 4 ++ .../input-validation-messages.const.ts | 1 + src/assets/i18n/en.json | 4 +- 7 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 src/app/features/preprints/components/stepper/author-assertion-step/array-input/array-input.component.html create mode 100644 src/app/features/preprints/components/stepper/author-assertion-step/array-input/array-input.component.scss create mode 100644 src/app/features/preprints/components/stepper/author-assertion-step/array-input/array-input.component.spec.ts create mode 100644 src/app/features/preprints/components/stepper/author-assertion-step/array-input/array-input.component.ts diff --git a/src/app/features/preprints/components/stepper/author-assertion-step/array-input/array-input.component.html b/src/app/features/preprints/components/stepper/author-assertion-step/array-input/array-input.component.html new file mode 100644 index 000000000..87227b291 --- /dev/null +++ b/src/app/features/preprints/components/stepper/author-assertion-step/array-input/array-input.component.html @@ -0,0 +1,17 @@ +
+
+ @let formArrayControls = formArray().controls; + @for (control of formArrayControls; track $index) { +
+ + @if (formArrayControls.length > 1) { + + } +
+ } +
+ +
+ +
+
diff --git a/src/app/features/preprints/components/stepper/author-assertion-step/array-input/array-input.component.scss b/src/app/features/preprints/components/stepper/author-assertion-step/array-input/array-input.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/features/preprints/components/stepper/author-assertion-step/array-input/array-input.component.spec.ts b/src/app/features/preprints/components/stepper/author-assertion-step/array-input/array-input.component.spec.ts new file mode 100644 index 000000000..b29274281 --- /dev/null +++ b/src/app/features/preprints/components/stepper/author-assertion-step/array-input/array-input.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ArrayInputComponent } from './array-input.component'; + +describe('ArrayInputComponent', () => { + let component: ArrayInputComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ArrayInputComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(ArrayInputComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/preprints/components/stepper/author-assertion-step/array-input/array-input.component.ts b/src/app/features/preprints/components/stepper/author-assertion-step/array-input/array-input.component.ts new file mode 100644 index 000000000..39f2fc55d --- /dev/null +++ b/src/app/features/preprints/components/stepper/author-assertion-step/array-input/array-input.component.ts @@ -0,0 +1,41 @@ +import { Button } from 'primeng/button'; + +import { ChangeDetectionStrategy, Component, input, OnInit } from '@angular/core'; +import { FormArray, FormControl, ReactiveFormsModule, ValidatorFn } from '@angular/forms'; + +import { TextInputComponent } from '@shared/components'; + +@Component({ + selector: 'osf-array-input', + imports: [ReactiveFormsModule, Button, TextInputComponent], + templateUrl: './array-input.component.html', + styleUrl: './array-input.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ArrayInputComponent implements OnInit { + formArray = input.required>(); + inputPlaceholder = input.required(); + validators = input.required(); + + ngOnInit() { + const formArray = this.formArray(); + if (formArray.controls.length === 0) { + this.add(); + } + } + + add() { + this.formArray().push( + new FormControl('', { + nonNullable: true, + validators: this.validators(), + }) + ); + } + + remove(index: number) { + if (this.formArray().length > 1) { + this.formArray().removeAt(index); + } + } +} diff --git a/src/app/shared/components/text-input/text-input.component.ts b/src/app/shared/components/text-input/text-input.component.ts index 3b37fb94d..4a7a027d0 100644 --- a/src/app/shared/components/text-input/text-input.component.ts +++ b/src/app/shared/components/text-input/text-input.component.ts @@ -43,6 +43,10 @@ export class TextInputComponent { return { key: INPUT_VALIDATION_MESSAGES.email }; } + if (errors['link']) { + return { key: INPUT_VALIDATION_MESSAGES.link }; + } + if (errors['maxlength']) return { key: INPUT_VALIDATION_MESSAGES.maxLength, diff --git a/src/app/shared/constants/input-validation-messages.const.ts b/src/app/shared/constants/input-validation-messages.const.ts index ea17320af..8655c9dbc 100644 --- a/src/app/shared/constants/input-validation-messages.const.ts +++ b/src/app/shared/constants/input-validation-messages.const.ts @@ -4,4 +4,5 @@ export const INPUT_VALIDATION_MESSAGES = { maxLength: 'validation.maxLength', minLength: 'validation.minLength', invalidInput: 'validation.invalidInput', + link: 'validation.link', }; diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 0b763e9d1..9dcf7d96b 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -415,7 +415,6 @@ "step2InfoText": "If your project includes components, you can select which components to include or exclude at the end of the registration." }, "selectProject": "Select your project", - "createDraft": "Create draft", "createdSuccessfully": "Draft created successfully" }, @@ -1758,7 +1757,8 @@ "email": "Please enter a valid email address.", "maxLength": "The field must be at most {{length}} characters.", "minLength": "The field must be at least {{length}} characters.", - "invalidInput": "Invalid input." + "invalidInput": "Invalid input.", + "link": "This field must start with http:// or https:// to be a valid url." }, "searchHelpTutorial": { "step1": { From d5477792732e84ba9b375b63590edc20a20707e2 Mon Sep 17 00:00:00 2001 From: Roma Date: Thu, 3 Jul 2025 19:33:42 +0300 Subject: [PATCH 15/18] feat(array-input): Used array-input for data links and prereg links --- .../author-assertions-step.component.html | 16 +++++++++++++++- .../author-assertions-step.component.ts | 18 ++++++++++++++---- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.html b/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.html index 690da765a..3bfdf6fac 100644 --- a/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.html +++ b/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.html @@ -124,7 +124,13 @@

Public Data

} } @else { -

implement this list of adding links

+
+ +
}

@@ -206,6 +212,14 @@

Public Preregistration

optionValue="value" [showClear]="false" /> + +
+ +
} diff --git a/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.ts b/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.ts index 981ffc7fb..06dc74e94 100644 --- a/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.ts +++ b/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.ts @@ -13,14 +13,16 @@ import { Tooltip } from 'primeng/tooltip'; import { NgClass } from '@angular/common'; import { ChangeDetectionStrategy, Component, effect, HostListener, inject, output } from '@angular/core'; import { toSignal } from '@angular/core/rxjs-interop'; -import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; +import { FormArray, FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { StringOrNull } from '@core/helpers'; +import { ArrayInputComponent } from '@osf/features/preprints/components/stepper/author-assertion-step/array-input/array-input.component'; import { formInputLimits } from '@osf/features/preprints/constants'; import { ApplicabilityStatus, PreregLinkInfo } from '@osf/features/preprints/enums'; import { SubmitPreprintSelectors, UpdatePreprint } from '@osf/features/preprints/store/submit-preprint'; import { INPUT_VALIDATION_MESSAGES } from '@shared/constants'; import { ToastService } from '@shared/services'; +import { CustomValidators } from '@shared/utils'; @Component({ selector: 'osf-author-assertions-step', @@ -36,6 +38,7 @@ import { ToastService } from '@shared/services'; Button, Tooltip, Select, + ArrayInputComponent, ], templateUrl: './author-assertions-step.component.html', styleUrl: './author-assertions-step.component.scss', @@ -47,6 +50,7 @@ export class AuthorAssertionsStepComponent { updatePreprint: UpdatePreprint, }); + readonly CustomValidators = CustomValidators; readonly ApplicabilityStatus = ApplicabilityStatus; readonly inputLimits = formInputLimits; readonly INPUT_VALIDATION_MESSAGES = INPUT_VALIDATION_MESSAGES; @@ -71,6 +75,9 @@ export class AuthorAssertionsStepComponent { nonNullable: true, validators: [], }), + dataLinks: new FormArray( + this.createdPreprint()!.dataLinks?.map((link) => new FormControl(link)) || [] + ), whyNoData: new FormControl(this.createdPreprint()!.whyNoData, { nonNullable: false, validators: [], @@ -79,6 +86,9 @@ export class AuthorAssertionsStepComponent { nonNullable: true, validators: [], }), + preregLinks: new FormArray( + this.createdPreprint()!.preregLinks?.map((link) => new FormControl(link)) || [] + ), whyNoPrereg: new FormControl(this.createdPreprint()!.whyNoPrereg, { nonNullable: false, validators: [], @@ -189,12 +199,12 @@ export class AuthorAssertionsStepComponent { const hasDataLinks = formValue.hasDataLinks; const whyNoData = formValue.whyNoData || null; - const dataLinks: string[] = []; + const dataLinks: string[] = formValue.dataLinks || []; const hasPreregLinks = formValue.hasPreregLinks; const whyNoPrereg = formValue.whyNoPrereg || null; - const preregLinks: string[] = []; - const preregLinkInfo = formValue.preregLinkInfo; + const preregLinks: string[] = formValue.preregLinks || []; + const preregLinkInfo = formValue.preregLinkInfo || undefined; this.actions .updatePreprint(this.createdPreprint()!.id, { From 83a6b8ed58c63d6a0b3ad89f753c3163d6f20850 Mon Sep 17 00:00:00 2001 From: Roma Date: Fri, 4 Jul 2025 12:20:31 +0300 Subject: [PATCH 16/18] refactor(author-assertions): Refactored step. Fixed minor issues --- .../array-input/array-input.component.ts | 11 +- .../author-assertions-step.component.html | 10 +- .../author-assertions-step.component.ts | 125 ++++++++++++------ .../models/preprint-json-api.models.ts | 8 +- .../preprints/models/preprint.models.ts | 8 +- 5 files changed, 96 insertions(+), 66 deletions(-) diff --git a/src/app/features/preprints/components/stepper/author-assertion-step/array-input/array-input.component.ts b/src/app/features/preprints/components/stepper/author-assertion-step/array-input/array-input.component.ts index 39f2fc55d..a65ad89f2 100644 --- a/src/app/features/preprints/components/stepper/author-assertion-step/array-input/array-input.component.ts +++ b/src/app/features/preprints/components/stepper/author-assertion-step/array-input/array-input.component.ts @@ -1,6 +1,6 @@ import { Button } from 'primeng/button'; -import { ChangeDetectionStrategy, Component, input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, input } from '@angular/core'; import { FormArray, FormControl, ReactiveFormsModule, ValidatorFn } from '@angular/forms'; import { TextInputComponent } from '@shared/components'; @@ -12,18 +12,11 @@ import { TextInputComponent } from '@shared/components'; styleUrl: './array-input.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, }) -export class ArrayInputComponent implements OnInit { +export class ArrayInputComponent { formArray = input.required>(); inputPlaceholder = input.required(); validators = input.required(); - ngOnInit() { - const formArray = this.formArray(); - if (formArray.controls.length === 0) { - this.add(); - } - } - add() { this.formArray().push( new FormControl('', { diff --git a/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.html b/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.html index 3bfdf6fac..dd7e22bb7 100644 --- a/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.html +++ b/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.html @@ -128,7 +128,7 @@

Public Data

} @@ -154,7 +154,7 @@

Public Preregistration

[value]="ApplicabilityStatus.Applicable" [formControl]="authorAssertionsForm.controls['hasPreregLinks']" /> - +
@@ -164,7 +164,7 @@

Public Preregistration

[value]="ApplicabilityStatus.Unavailable" [formControl]="authorAssertionsForm.controls['hasPreregLinks']" /> - +
@@ -174,7 +174,7 @@

Public Preregistration

[value]="ApplicabilityStatus.NotApplicable" [formControl]="authorAssertionsForm.controls['hasPreregLinks']" /> - +
@let hasPreregLinks = authorAssertionsForm.value.hasPreregLinks!; @@ -217,7 +217,7 @@

Public Preregistration

} diff --git a/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.ts b/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.ts index 06dc74e94..9af511445 100644 --- a/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.ts +++ b/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.ts @@ -13,7 +13,16 @@ import { Tooltip } from 'primeng/tooltip'; import { NgClass } from '@angular/common'; import { ChangeDetectionStrategy, Component, effect, HostListener, inject, output } from '@angular/core'; import { toSignal } from '@angular/core/rxjs-interop'; -import { FormArray, FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; +import { + AbstractControl, + FormArray, + FormControl, + FormGroup, + FormsModule, + ReactiveFormsModule, + ValidatorFn, + Validators, +} from '@angular/forms'; import { StringOrNull } from '@core/helpers'; import { ArrayInputComponent } from '@osf/features/preprints/components/stepper/author-assertion-step/array-input/array-input.component'; @@ -58,12 +67,13 @@ export class AuthorAssertionsStepComponent { label: key, value, })); + readonly linkValidators = [CustomValidators.linkValidator(), CustomValidators.requiredTrimmed()]; createdPreprint = select(SubmitPreprintSelectors.getCreatedPreprint); isUpdatingPreprint = select(SubmitPreprintSelectors.isPreprintSubmitting); readonly authorAssertionsForm = new FormGroup({ - hasCoi: new FormControl(this.createdPreprint()!.hasCoi, { + hasCoi: new FormControl(this.createdPreprint()!.hasCoi || false, { nonNullable: true, validators: [], }), @@ -71,10 +81,13 @@ export class AuthorAssertionsStepComponent { nonNullable: false, validators: [], }), - hasDataLinks: new FormControl(this.createdPreprint()!.hasDataLinks, { - nonNullable: true, - validators: [], - }), + hasDataLinks: new FormControl( + this.createdPreprint()!.hasDataLinks || ApplicabilityStatus.NotApplicable, + { + nonNullable: true, + validators: [], + } + ), dataLinks: new FormArray( this.createdPreprint()!.dataLinks?.map((link) => new FormControl(link)) || [] ), @@ -82,10 +95,13 @@ export class AuthorAssertionsStepComponent { nonNullable: false, validators: [], }), - hasPreregLinks: new FormControl(this.createdPreprint()!.hasPreregLinks, { - nonNullable: true, - validators: [], - }), + hasPreregLinks: new FormControl( + this.createdPreprint()!.hasPreregLinks || ApplicabilityStatus.NotApplicable, + { + nonNullable: true, + validators: [], + } + ), preregLinks: new FormArray( this.createdPreprint()!.preregLinks?.map((link) => new FormControl(link)) || [] ), @@ -100,13 +116,13 @@ export class AuthorAssertionsStepComponent { }); hasCoiValue = toSignal(this.authorAssertionsForm.controls['hasCoi'].valueChanges, { - initialValue: this.createdPreprint()!.hasCoi, + initialValue: this.createdPreprint()!.hasCoi || false, }); hasDataLinks = toSignal(this.authorAssertionsForm.controls['hasDataLinks'].valueChanges, { - initialValue: this.createdPreprint()!.hasDataLinks, + initialValue: this.createdPreprint()!.hasDataLinks || ApplicabilityStatus.NotApplicable, }); hasPreregLinks = toSignal(this.authorAssertionsForm.controls['hasPreregLinks'].valueChanges, { - initialValue: this.createdPreprint()!.hasPreregLinks, + initialValue: this.createdPreprint()!.hasPreregLinks || ApplicabilityStatus.NotApplicable, }); nextClicked = output(); @@ -117,12 +133,9 @@ export class AuthorAssertionsStepComponent { const coiStatementControl = this.authorAssertionsForm.controls['coiStatement']; if (hasCoi) { - coiStatementControl.setValidators([Validators.required]); - coiStatementControl.enable(); + this.enableAndSetValidators(coiStatementControl, [Validators.required]); } else { - coiStatementControl.clearValidators(); - coiStatementControl.setValue(null); - coiStatementControl.disable(); + this.disableAndClearValidators(coiStatementControl); } coiStatementControl.updateValueAndValidity(); @@ -131,57 +144,52 @@ export class AuthorAssertionsStepComponent { effect(() => { const hasDataLinks = this.hasDataLinks(); const whyNoDataControl = this.authorAssertionsForm.controls['whyNoData']; + const dataLinksControl = this.authorAssertionsForm.controls['dataLinks']; switch (hasDataLinks) { case ApplicabilityStatus.Unavailable: - whyNoDataControl.setValidators([Validators.required]); - whyNoDataControl.enable(); + this.enableAndSetValidators(whyNoDataControl, [Validators.required]); + this.disableAndClearValidators(dataLinksControl); break; case ApplicabilityStatus.NotApplicable: - whyNoDataControl.clearValidators(); - whyNoDataControl.setValue(null); - whyNoDataControl.disable(); + this.disableAndClearValidators(whyNoDataControl); + this.disableAndClearValidators(dataLinksControl); break; case ApplicabilityStatus.Applicable: - whyNoDataControl.clearValidators(); - whyNoDataControl.setValue(null); - whyNoDataControl.disable(); + this.disableAndClearValidators(whyNoDataControl); + this.addAtLeastOneControl(dataLinksControl); break; } whyNoDataControl.updateValueAndValidity(); + dataLinksControl.updateValueAndValidity(); }); effect(() => { - const hasDataLinks = this.hasPreregLinks(); + const hasPreregLinks = this.hasPreregLinks(); const whyNoPreregControl = this.authorAssertionsForm.controls['whyNoPrereg']; const preregLinkInfoControl = this.authorAssertionsForm.controls['preregLinkInfo']; + const preregLinksControl = this.authorAssertionsForm.controls['preregLinks']; - switch (hasDataLinks) { + switch (hasPreregLinks) { case ApplicabilityStatus.Unavailable: - whyNoPreregControl.setValidators([Validators.required]); - whyNoPreregControl.enable(); - - preregLinkInfoControl.clearValidators(); - preregLinkInfoControl.setValue(null); + this.enableAndSetValidators(whyNoPreregControl, [Validators.required]); + this.disableAndClearValidators(preregLinkInfoControl); + this.disableAndClearValidators(preregLinksControl); break; case ApplicabilityStatus.NotApplicable: - whyNoPreregControl.clearValidators(); - whyNoPreregControl.setValue(null); - whyNoPreregControl.disable(); - - preregLinkInfoControl.clearValidators(); - preregLinkInfoControl.setValue(null); + this.disableAndClearValidators(whyNoPreregControl); + this.disableAndClearValidators(preregLinkInfoControl); + this.disableAndClearValidators(preregLinksControl); break; case ApplicabilityStatus.Applicable: - whyNoPreregControl.clearValidators(); - whyNoPreregControl.setValue(null); - whyNoPreregControl.disable(); - - preregLinkInfoControl.setValidators([Validators.required]); + this.disableAndClearValidators(whyNoPreregControl); + this.enableAndSetValidators(preregLinkInfoControl, [Validators.required]); + this.addAtLeastOneControl(preregLinksControl); break; } whyNoPreregControl.updateValueAndValidity(); preregLinkInfoControl.updateValueAndValidity(); + preregLinksControl.updateValueAndValidity(); }); } @@ -225,4 +233,33 @@ export class AuthorAssertionsStepComponent { }, }); } + + private disableAndClearValidators(control: AbstractControl) { + if (control instanceof FormArray) { + while (control.length !== 0) { + control.removeAt(0); + } + return; + } + + control.clearValidators(); + control.setValue(null); + control.disable(); + } + + private enableAndSetValidators(control: AbstractControl, validators: ValidatorFn[]) { + control.setValidators(validators); + control.enable(); + } + + private addAtLeastOneControl(formArray: FormArray) { + if (formArray.controls.length > 0) return; + + formArray.push( + new FormControl('', { + nonNullable: true, + validators: this.linkValidators, + }) + ); + } } diff --git a/src/app/features/preprints/models/preprint-json-api.models.ts b/src/app/features/preprints/models/preprint-json-api.models.ts index 27877a0a5..44ae1cd64 100644 --- a/src/app/features/preprints/models/preprint-json-api.models.ts +++ b/src/app/features/preprints/models/preprint-json-api.models.ts @@ -1,4 +1,4 @@ -import { StringOrNull } from '@core/helpers'; +import { BooleanOrNull, StringOrNull } from '@core/helpers'; import { ApplicabilityStatus, PreregLinkInfo } from '@osf/features/preprints/enums'; import { LicenseRecordJsonApi } from '@shared/models'; @@ -23,12 +23,12 @@ export interface PreprintJsonApi { date_last_transitioned: Date | null; version: number; is_latest_version: boolean; - has_coi: boolean; + has_coi: BooleanOrNull; conflict_of_interest_statement: StringOrNull; - has_data_links: ApplicabilityStatus; + has_data_links: ApplicabilityStatus | null; data_links: string[]; why_no_data: StringOrNull; - has_prereg_links: ApplicabilityStatus; + has_prereg_links: ApplicabilityStatus | null; why_no_prereg: StringOrNull; prereg_links: string[]; prereg_link_info: PreregLinkInfo | null; diff --git a/src/app/features/preprints/models/preprint.models.ts b/src/app/features/preprints/models/preprint.models.ts index bba170678..7f797cd77 100644 --- a/src/app/features/preprints/models/preprint.models.ts +++ b/src/app/features/preprints/models/preprint.models.ts @@ -1,4 +1,4 @@ -import { StringOrNull } from '@core/helpers'; +import { BooleanOrNull, StringOrNull } from '@core/helpers'; import { ApplicabilityStatus, PreregLinkInfo } from '@osf/features/preprints/enums'; import { LicenseOptions } from '@shared/models'; @@ -19,12 +19,12 @@ export interface Preprint { primaryFileId: StringOrNull; licenseId: StringOrNull; licenseOptions: LicenseOptions | null; - hasCoi: boolean; + hasCoi: BooleanOrNull; coiStatement: StringOrNull; - hasDataLinks: ApplicabilityStatus; + hasDataLinks: ApplicabilityStatus | null; dataLinks: string[]; whyNoData: StringOrNull; - hasPreregLinks: ApplicabilityStatus; + hasPreregLinks: ApplicabilityStatus | null; whyNoPrereg: StringOrNull; preregLinks: string[]; preregLinkInfo: PreregLinkInfo | null; From 26e4f44807db3cb82267bb3b837b2490a55ea589 Mon Sep 17 00:00:00 2001 From: Roma Date: Fri, 4 Jul 2025 13:04:39 +0300 Subject: [PATCH 17/18] fix(translations): Update URL validation message for clarity --- src/assets/i18n/en.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 02b867e76..ad612fb3b 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -1610,7 +1610,6 @@ "step2InfoText": "If your project includes components, you can select which components to include or exclude at the end of the registration." }, "selectProject": "Select your project", - "createDraft": "Create draft", "createdSuccessfully": "Draft created successfully" }, @@ -1746,7 +1745,7 @@ "maxLength": "The field must be at most {{length}} characters.", "minLength": "The field must be at least {{length}} characters.", "invalidInput": "Invalid input.", - "link": "This field must https:// to be a valid url." + "link": "This field must start with https:// to be a valid url." }, "searchHelpTutorial": { "step1": { From 395976404245048a0a550d41f4d8aec2fdd3c415 Mon Sep 17 00:00:00 2001 From: Roma Date: Fri, 4 Jul 2025 13:42:05 +0300 Subject: [PATCH 18/18] fix(comments): Fixed comments --- .../author-assertion-step/author-assertions-step.component.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.scss b/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.scss index 18b6612e7..243cc50eb 100644 --- a/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.scss +++ b/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.scss @@ -1,4 +1,3 @@ -@use "assets/styles/mixins" as mix; @use "assets/styles/variables" as var; .card {