From 7b2fe6ea3684ef8edf7a473bc88ee7a99fc52c78 Mon Sep 17 00:00:00 2001 From: nsemets Date: Thu, 4 Sep 2025 12:32:53 +0300 Subject: [PATCH 1/2] fix(tooltip): added tooltip to next button --- .../components/stepper/file-step/file-step.component.html | 8 +++++++- src/assets/i18n/en.json | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/app/features/preprints/components/stepper/file-step/file-step.component.html b/src/app/features/preprints/components/stepper/file-step/file-step.component.html index 5c1947e69..616d5e78b 100644 --- a/src/app/features/preprints/components/stepper/file-step/file-step.component.html +++ b/src/app/features/preprints/components/stepper/file-step/file-step.component.html @@ -128,7 +128,13 @@

{{ 'preprints.preprintStepper.file.title' | translate }}

class="w-6 md:w-9rem" styleClass="w-full" [label]="'common.buttons.next' | translate" - (onClick)="nextButtonClicked()" [disabled]="!preprint()?.primaryFileId || versionFileMode()" + [pTooltip]=" + !preprint()?.primaryFileId || versionFileMode() + ? ('preprints.preprintStepper.common.validation.fillRequiredFields' | translate) + : '' + " + tooltipPosition="top" + (onClick)="nextButtonClicked()" /> diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 68955053d..eec9fc31a 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -2037,7 +2037,7 @@ }, "common": { "validation": { - "fillRequiredFields": "Fill in 'Required' fields to continue" + "fillRequiredFields": "Fill in “Required” fields to continue" }, "successMessages": { "preprintSaved": "Preprint saved", From 886f0ad5c03f9cb2245620c085411fb7a424f6b9 Mon Sep 17 00:00:00 2001 From: nsemets Date: Thu, 4 Sep 2025 13:11:02 +0300 Subject: [PATCH 2/2] fix(emails): fixed emails bug --- .../store/user-emails/user-emails.state.ts | 24 +++++++++++++++---- .../registrations/registrations.component.ts | 8 ++++--- .../services/registrations.service.ts | 9 ++++--- .../store/registrations.model.ts | 12 ++++++++-- .../store/registrations.selectors.ts | 5 ---- .../store/registrations.state.ts | 13 ++-------- .../connected-emails.component.html | 2 ++ .../connected-emails.component.ts | 4 +++- 8 files changed, 46 insertions(+), 31 deletions(-) diff --git a/src/app/core/store/user-emails/user-emails.state.ts b/src/app/core/store/user-emails/user-emails.state.ts index 12508728c..3b1c176c8 100644 --- a/src/app/core/store/user-emails/user-emails.state.ts +++ b/src/app/core/store/user-emails/user-emails.state.ts @@ -1,6 +1,6 @@ import { Action, State, StateContext, Store } from '@ngxs/store'; -import { catchError, tap, throwError } from 'rxjs'; +import { catchError, tap } from 'rxjs'; import { inject, Injectable } from '@angular/core'; @@ -140,9 +140,25 @@ export class UserEmailsState { @Action(ResendConfirmation) resendConfirmation(ctx: StateContext, action: ResendConfirmation) { - return this.userEmailsService - .resendConfirmation(action.emailId) - .pipe(catchError((error) => throwError(() => error))); + ctx.patchState({ + emails: { + ...ctx.getState().emails, + isSubmitting: true, + error: null, + }, + }); + + return this.userEmailsService.resendConfirmation(action.emailId).pipe( + tap(() => { + ctx.patchState({ + emails: { + ...ctx.getState().emails, + isSubmitting: false, + }, + }); + }), + catchError((error) => handleSectionError(ctx, 'emails', error)) + ); } @Action(MakePrimary) diff --git a/src/app/features/project/registrations/registrations.component.ts b/src/app/features/project/registrations/registrations.component.ts index 0ea5be665..69cf1e5c5 100644 --- a/src/app/features/project/registrations/registrations.component.ts +++ b/src/app/features/project/registrations/registrations.component.ts @@ -28,10 +28,12 @@ import { environment } from 'src/environments/environment'; export class RegistrationsComponent implements OnInit { private readonly route = inject(ActivatedRoute); private readonly router = inject(Router); + readonly projectId = toSignal(this.route.parent?.params.pipe(map((params) => params['id'])) ?? of(undefined)); - protected registrations = select(RegistrationsSelectors.getRegistrations); - protected isRegistrationsLoading = select(RegistrationsSelectors.isRegistrationsLoading); - protected actions = createDispatchMap({ getRegistrations: GetRegistrations }); + + registrations = select(RegistrationsSelectors.getRegistrations); + isRegistrationsLoading = select(RegistrationsSelectors.isRegistrationsLoading); + actions = createDispatchMap({ getRegistrations: GetRegistrations }); ngOnInit(): void { this.actions.getRegistrations(this.projectId()); diff --git a/src/app/features/project/registrations/services/registrations.service.ts b/src/app/features/project/registrations/services/registrations.service.ts index c536069b5..b41f388e5 100644 --- a/src/app/features/project/registrations/services/registrations.service.ts +++ b/src/app/features/project/registrations/services/registrations.service.ts @@ -3,7 +3,7 @@ import { map, Observable } from 'rxjs'; import { inject, Injectable } from '@angular/core'; import { RegistrationMapper } from '@osf/shared/mappers/registration'; -import { RegistrationCard, RegistrationDataJsonApi, ResponseJsonApi } from '@osf/shared/models'; +import { PaginatedData, RegistrationCard, RegistrationDataJsonApi, ResponseJsonApi } from '@osf/shared/models'; import { JsonApiService } from '@osf/shared/services'; import { environment } from 'src/environments/environment'; @@ -14,10 +14,9 @@ import { environment } from 'src/environments/environment'; export class RegistrationsService { private readonly jsonApiService = inject(JsonApiService); - getRegistrations(projectId: string): Observable<{ data: RegistrationCard[]; totalCount: number }> { - const params: Record = { - embed: 'contributors', - }; + getRegistrations(projectId: string): Observable> { + const params: Record = { embed: 'contributors' }; + const url = `${environment.apiUrl}/nodes/${projectId}/linked_by_registrations/`; return this.jsonApiService.get>(url, params).pipe( diff --git a/src/app/features/project/registrations/store/registrations.model.ts b/src/app/features/project/registrations/store/registrations.model.ts index 63fb02293..31a0ea5c0 100644 --- a/src/app/features/project/registrations/store/registrations.model.ts +++ b/src/app/features/project/registrations/store/registrations.model.ts @@ -1,6 +1,14 @@ -import { RegistrationCard } from '@osf/shared/models'; -import { AsyncStateWithTotalCount } from '@osf/shared/models/store'; +import { AsyncStateWithTotalCount, RegistrationCard } from '@osf/shared/models'; export interface RegistrationsStateModel { registrations: AsyncStateWithTotalCount; } + +export const REGISTRATIONS_STATE_DEFAULTS: RegistrationsStateModel = { + registrations: { + data: [], + isLoading: false, + error: null, + totalCount: 0, + }, +}; diff --git a/src/app/features/project/registrations/store/registrations.selectors.ts b/src/app/features/project/registrations/store/registrations.selectors.ts index 4bc6e5a01..f958bf568 100644 --- a/src/app/features/project/registrations/store/registrations.selectors.ts +++ b/src/app/features/project/registrations/store/registrations.selectors.ts @@ -13,9 +13,4 @@ export class RegistrationsSelectors { static isRegistrationsLoading(state: RegistrationsStateModel) { return state.registrations.isLoading; } - - @Selector([RegistrationsState]) - static getRegistrationsError(state: RegistrationsStateModel) { - return state.registrations.error; - } } diff --git a/src/app/features/project/registrations/store/registrations.state.ts b/src/app/features/project/registrations/store/registrations.state.ts index 40c119f15..0fd503eee 100644 --- a/src/app/features/project/registrations/store/registrations.state.ts +++ b/src/app/features/project/registrations/store/registrations.state.ts @@ -9,18 +9,11 @@ import { handleSectionError } from '@osf/shared/helpers'; import { RegistrationsService } from '../services'; import { GetRegistrations } from './registrations.actions'; -import { RegistrationsStateModel } from './registrations.model'; +import { REGISTRATIONS_STATE_DEFAULTS, RegistrationsStateModel } from './registrations.model'; @State({ name: 'registrations', - defaults: { - registrations: { - data: [], - isLoading: false, - error: null, - totalCount: 0, - }, - }, + defaults: REGISTRATIONS_STATE_DEFAULTS, }) @Injectable() export class RegistrationsState { @@ -36,9 +29,7 @@ export class RegistrationsState { return this.registrationsService.getRegistrations(action.projectId).pipe( tap((registrations) => { - const state = ctx.getState(); ctx.setState({ - ...state, registrations: { data: registrations.data, isLoading: false, diff --git a/src/app/features/settings/account-settings/components/connected-emails/connected-emails.component.html b/src/app/features/settings/account-settings/components/connected-emails/connected-emails.component.html index 211da1c76..594e45501 100644 --- a/src/app/features/settings/account-settings/components/connected-emails/connected-emails.component.html +++ b/src/app/features/settings/account-settings/components/connected-emails/connected-emails.component.html @@ -70,6 +70,8 @@

{{ 'settings.accountSettings.connectedEmails.title' | translate }}

) | translate " severity="secondary" + [disabled]="isEmailsSubmitting()" + [loading]="isEmailsSubmitting()" (click)="resendConfirmation(email)" > diff --git a/src/app/features/settings/account-settings/components/connected-emails/connected-emails.component.ts b/src/app/features/settings/account-settings/components/connected-emails/connected-emails.component.ts index e10027e58..a4f833163 100644 --- a/src/app/features/settings/account-settings/components/connected-emails/connected-emails.component.ts +++ b/src/app/features/settings/account-settings/components/connected-emails/connected-emails.component.ts @@ -7,7 +7,7 @@ import { Card } from 'primeng/card'; import { DialogService } from 'primeng/dynamicdialog'; import { Skeleton } from 'primeng/skeleton'; -import { filter, finalize } from 'rxjs'; +import { filter, finalize, throttleTime } from 'rxjs'; import { ChangeDetectionStrategy, Component, computed, DestroyRef, inject } from '@angular/core'; import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop'; @@ -42,6 +42,7 @@ export class ConnectedEmailsComponent { protected readonly currentUser = select(UserSelectors.getCurrentUser); protected readonly emails = select(UserEmailsSelectors.getEmails); protected readonly isEmailsLoading = select(UserEmailsSelectors.isEmailsLoading); + protected readonly isEmailsSubmitting = select(UserEmailsSelectors.isEmailsSubmitting); private readonly actions = createDispatchMap({ resendConfirmation: ResendConfirmation, @@ -98,6 +99,7 @@ export class ConnectedEmailsComponent { this.actions .resendConfirmation(email.id) .pipe( + throttleTime(2000), finalize(() => this.loaderService.hide()), takeUntilDestroyed(this.destroyRef) )