diff --git a/src/app/core/store/user/user.actions.ts b/src/app/core/store/user/user.actions.ts index 591b586bb..716f9c09f 100644 --- a/src/app/core/store/user/user.actions.ts +++ b/src/app/core/store/user/user.actions.ts @@ -24,25 +24,25 @@ export class UpdateUserSettings { export class UpdateProfileSettingsEmployment { static readonly type = '[Profile Settings] Update Employment'; - constructor(public payload: { employment: Employment[] }) {} + constructor(public payload: Employment[]) {} } export class UpdateProfileSettingsEducation { static readonly type = '[Profile Settings] Update Education'; - constructor(public payload: { education: Education[] }) {} + constructor(public payload: Education[]) {} } export class UpdateProfileSettingsSocialLinks { static readonly type = '[Profile Settings] Update Social Links'; - constructor(public payload: { socialLinks: Partial[] }) {} + constructor(public payload: Partial[]) {} } export class UpdateProfileSettingsUser { static readonly type = '[Profile Settings] Update User'; - constructor(public payload: { user: Partial }) {} + constructor(public payload: Partial) {} } export class SetUserAsModerator { diff --git a/src/app/core/store/user/user.state.ts b/src/app/core/store/user/user.state.ts index 8b23a6aea..70d30969d 100644 --- a/src/app/core/store/user/user.state.ts +++ b/src/app/core/store/user/user.state.ts @@ -135,7 +135,7 @@ export class UserState { return; } - const withoutNulls = payload.employment.map((item) => removeNullable(item)); + const withoutNulls = payload.map((item) => removeNullable(item)); return this.userService.updateUserProfile(userId, ProfileSettingsKey.Employment, withoutNulls).pipe( tap((user) => { @@ -160,7 +160,7 @@ export class UserState { return; } - const withoutNulls = payload.education.map((item) => removeNullable(item)); + const withoutNulls = payload.map((item) => removeNullable(item)); return this.userService.updateUserProfile(userId, ProfileSettingsKey.Education, withoutNulls).pipe( tap((user) => { @@ -185,7 +185,7 @@ export class UserState { return; } - const withoutNulls = UserMapper.toNamesRequest(removeNullable(payload.user)); + const withoutNulls = UserMapper.toNamesRequest(removeNullable(payload)); return this.userService.updateUserProfile(userId, ProfileSettingsKey.User, withoutNulls).pipe( tap((user) => { @@ -212,7 +212,7 @@ export class UserState { let social = {} as Partial; - payload.socialLinks.forEach((item) => { + payload.forEach((item) => { social = { ...social, ...item, diff --git a/src/app/features/collections/components/add-to-collection/project-contributors-step/project-contributors-step.component.html b/src/app/features/collections/components/add-to-collection/project-contributors-step/project-contributors-step.component.html index f093255b4..3bb5e8140 100644 --- a/src/app/features/collections/components/add-to-collection/project-contributors-step/project-contributors-step.component.html +++ b/src/app/features/collections/components/add-to-collection/project-contributors-step/project-contributors-step.component.html @@ -10,7 +10,7 @@

{{ 'collections.addToCollection.projectContributors' | translate }}

diff --git a/src/app/features/files/components/file-revisions/file-revisions.component.html b/src/app/features/files/components/file-revisions/file-revisions.component.html index 2c7e02e54..b67282be0 100644 --- a/src/app/features/files/components/file-revisions/file-revisions.component.html +++ b/src/app/features/files/components/file-revisions/file-revisions.component.html @@ -23,7 +23,7 @@

{{ 'files.detail.revisions.title' | translate }}

> @@ -36,7 +36,7 @@

{{ 'files.detail.revisions.title' | translate }}

> diff --git a/src/app/features/settings/developer-apps/pages/developer-app-details/developer-app-details.component.spec.ts b/src/app/features/settings/developer-apps/pages/developer-app-details/developer-app-details.component.spec.ts index 51995ad5c..e59555ae9 100644 --- a/src/app/features/settings/developer-apps/pages/developer-app-details/developer-app-details.component.spec.ts +++ b/src/app/features/settings/developer-apps/pages/developer-app-details/developer-app-details.component.spec.ts @@ -12,8 +12,7 @@ import { provideHttpClientTesting } from '@angular/common/http/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; -import { IS_XSMALL } from '@osf/shared/helpers'; -import { CustomConfirmationService } from '@osf/shared/services'; +import { CustomConfirmationService, ToastService } from '@osf/shared/services'; import { DeveloperAppsState } from '../../store'; @@ -42,7 +41,7 @@ describe('DeveloperAppDetailsComponent', () => { MockProvider(ActivatedRoute, { params: of({ id: 'test-client-id' }) }), MockProvider(Router, mockRouter), MockProvider(CustomConfirmationService), - MockProvider(IS_XSMALL, of(false)), + MockProvider(ToastService), ], }).compileComponents(); diff --git a/src/app/features/settings/developer-apps/pages/developer-apps-list/developer-apps-list.component.spec.ts b/src/app/features/settings/developer-apps/pages/developer-apps-list/developer-apps-list.component.spec.ts index e54c506e5..1babc448f 100644 --- a/src/app/features/settings/developer-apps/pages/developer-apps-list/developer-apps-list.component.spec.ts +++ b/src/app/features/settings/developer-apps/pages/developer-apps-list/developer-apps-list.component.spec.ts @@ -9,7 +9,7 @@ import { provideHttpClient } from '@angular/common/http'; import { provideHttpClientTesting } from '@angular/common/http/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { CustomConfirmationService } from '@osf/shared/services'; +import { CustomConfirmationService, ToastService } from '@osf/shared/services'; import { MOCK_DEVELOPER_APP } from '@shared/mocks/developer-app.mock'; import { DeveloperAppsState } from '../../store'; @@ -31,6 +31,7 @@ describe('DeveloperApplicationsListComponent', () => { MockProvider(ConfirmationService), MockProvider(TranslateService), MockProvider(CustomConfirmationService), + MockProvider(ToastService), ], }).compileComponents(); diff --git a/src/app/features/settings/notifications/notifications.component.html b/src/app/features/settings/notifications/notifications.component.html index cc51848d8..c24010149 100644 --- a/src/app/features/settings/notifications/notifications.component.html +++ b/src/app/features/settings/notifications/notifications.component.html @@ -76,7 +76,7 @@

{{ 'settings.notifications.emailPreferences.title' | translate }}

{{ 'settings.notifications.notificationPreferences.title' | translate }}

diff --git a/src/app/features/settings/notifications/notifications.component.spec.ts b/src/app/features/settings/notifications/notifications.component.spec.ts index 3d8c60051..192cb7d70 100644 --- a/src/app/features/settings/notifications/notifications.component.spec.ts +++ b/src/app/features/settings/notifications/notifications.component.spec.ts @@ -14,7 +14,7 @@ import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; import { UserSelectors } from '@osf/core/store/user'; import { LoaderService, ToastService } from '@osf/shared/services'; import { SubscriptionEvent, SubscriptionFrequency } from '@shared/enums'; -import { MOCK_STORE, MOCK_USER } from '@shared/mocks'; +import { MOCK_STORE, MOCK_USER, TranslateServiceMock } from '@shared/mocks'; import { UserSettings } from '@shared/models'; import { NotificationsComponent } from './notifications.component'; @@ -31,7 +31,7 @@ describe('NotificationsComponent', () => { }; const mockNotificationSubscriptions = [ - { id: 'id1', event: SubscriptionEvent.GlobalComments, frequency: SubscriptionFrequency.Daily }, + { id: 'id1', event: SubscriptionEvent.GlobalMentions, frequency: SubscriptionFrequency.Daily }, { id: 'id2', event: SubscriptionEvent.GlobalMentions, @@ -79,7 +79,7 @@ describe('NotificationsComponent', () => { providers: [ provideHttpClient(), provideHttpClientTesting(), - MockProvider(TranslatePipe), + TranslateServiceMock, MockProvider(Store, MOCK_STORE), MockProvider(LoaderService, mockLoaderService), MockProvider(ToastService, mockToastService), @@ -127,7 +127,7 @@ describe('NotificationsComponent', () => { it('should call dispatch only once per subscription change', () => { const mockDispatch = jest.fn().mockReturnValue(of({})); MOCK_STORE.dispatch.mockImplementation(mockDispatch); - const event = SubscriptionEvent.GlobalComments; + const event = SubscriptionEvent.GlobalMentions; const frequency = SubscriptionFrequency.Daily; component.onSubscriptionChange(event, frequency); diff --git a/src/app/features/settings/profile-settings/components/education-form/education-form.component.html b/src/app/features/settings/profile-settings/components/education-form/education-form.component.html index 5fe9ca914..d839c628f 100644 --- a/src/app/features/settings/profile-settings/components/education-form/education-form.component.html +++ b/src/app/features/settings/profile-settings/components/education-form/education-form.component.html @@ -52,9 +52,16 @@

[dateFormat]="dateFormat" [iconDisplay]="'input'" [showIcon]="true" + view="month" [maxDate]="maxDate" [minDate]="minDate" > + + @if (startDateRequiredError) { + + {{ 'validation.required' | translate }} + + } @if (!group().controls['ongoing'].value) { @@ -68,10 +75,17 @@

formControlName="endDate" [dateFormat]="dateFormat" [iconDisplay]="'input'" + view="month" [showIcon]="true" [maxDate]="maxDate" [minDate]="minDate" > + + @if (endDateRequiredError) { + + {{ 'validation.required' | translate }} + + } } @@ -91,7 +105,7 @@

@if (isDateError) { - + {{ 'settings.profileSettings.endDateError' | translate }} } diff --git a/src/app/features/settings/profile-settings/components/education-form/education-form.component.ts b/src/app/features/settings/profile-settings/components/education-form/education-form.component.ts index 0bfeab450..86a4e0762 100644 --- a/src/app/features/settings/profile-settings/components/education-form/education-form.component.ts +++ b/src/app/features/settings/profile-settings/components/education-form/education-form.component.ts @@ -6,11 +6,9 @@ import { DatePicker } from 'primeng/datepicker'; import { InputText } from 'primeng/inputtext'; import { Message } from 'primeng/message'; -import { filter } from 'rxjs'; - import { ChangeDetectionStrategy, Component, DestroyRef, inject, input, OnInit, output } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; +import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; import { TextInputComponent } from '@osf/shared/components'; import { InputLimits } from '@osf/shared/constants'; @@ -28,7 +26,7 @@ export class EducationFormComponent implements OnInit { readonly maxDate = MAX_DATE; readonly minDate = MIN_DATE; readonly institutionMaxLength = InputLimits.fullName.maxLength; - readonly dateFormat = 'mm/dd/yy'; + readonly dateFormat = 'mm/yy'; private readonly destroyRef = inject(DestroyRef); @@ -46,12 +44,30 @@ export class EducationFormComponent implements OnInit { return form.errors && form.errors['dateRangeInvalid']; } + get startDateRequiredError() { + const control = this.group().controls['startDate']; + return control.invalid && control.errors?.['required'] && (control.touched || control.dirty); + } + + get endDateRequiredError() { + const control = this.group().controls['endDate']; + return control.invalid && control.errors?.['required'] && (control.touched || control.dirty); + } + ngOnInit() { this.group() - .controls['ongoing'].valueChanges.pipe( - filter((res) => !!res), - takeUntilDestroyed(this.destroyRef) - ) - .subscribe(() => this.group().controls['endDate'].setValue(null)); + .controls['ongoing'].valueChanges.pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe((ongoing) => { + const endDateControl = this.group().controls['endDate']; + + if (ongoing) { + endDateControl.setValue(null); + endDateControl.clearValidators(); + } else { + endDateControl.setValidators([Validators.required]); + } + + endDateControl.updateValueAndValidity(); + }); } } diff --git a/src/app/features/settings/profile-settings/components/education/education.component.spec.ts b/src/app/features/settings/profile-settings/components/education/education.component.spec.ts index 862d4318c..cf459628d 100644 --- a/src/app/features/settings/profile-settings/components/education/education.component.spec.ts +++ b/src/app/features/settings/profile-settings/components/education/education.component.spec.ts @@ -125,30 +125,28 @@ describe('EducationComponent', () => { component.saveEducation(); expect(mockStore.dispatch).toHaveBeenCalledWith( - new UpdateProfileSettingsEducation({ - education: [ - { - institution: 'Test University', - department: 'Engineering', - degree: 'Bachelor', - startYear: 2020, - startMonth: 0, - endYear: 2024, - endMonth: 6, - ongoing: false, - }, - { - institution: 'Advanced University', - department: 'Software Engineering', - degree: 'Master of Science', - startYear: 2020, - startMonth: 8, - endYear: null, - endMonth: null, - ongoing: false, - }, - ], - }) + new UpdateProfileSettingsEducation([ + { + institution: 'Test University', + department: 'Engineering', + degree: 'Bachelor', + startYear: 2020, + startMonth: 1, + endYear: 2024, + endMonth: 6, + ongoing: false, + }, + { + institution: 'Advanced University', + department: 'Software Engineering', + degree: 'Master of Science', + startYear: 2020, + startMonth: 9, + endYear: 2025, + endMonth: 8, + ongoing: false, + }, + ]) ); }); }); diff --git a/src/app/features/settings/profile-settings/components/education/education.component.ts b/src/app/features/settings/profile-settings/components/education/education.component.ts index d8864c649..4ffe3bae2 100644 --- a/src/app/features/settings/profile-settings/components/education/education.component.ts +++ b/src/app/features/settings/profile-settings/components/education/education.component.ts @@ -14,7 +14,7 @@ import { inject, } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { FormArray, FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms'; +import { FormArray, FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; import { UpdateProfileSettingsEducation, UserSelectors } from '@osf/core/store/user'; import { CustomValidators } from '@osf/shared/helpers'; @@ -96,7 +96,7 @@ export class EducationComponent { this.loaderService.show(); this.actions - .updateProfileSettingsEducation({ education: formattedEducation }) + .updateProfileSettingsEducation(formattedEducation) .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe({ next: () => { @@ -132,16 +132,19 @@ export class EducationComponent { } private createEducationFormGroup(education?: Partial): FormGroup { + const isOngoing = education?.ongoing ?? false; + const endDateValidators = isOngoing ? [] : [Validators.required]; + return this.fb.group( { institution: [education?.institution ?? '', CustomValidators.requiredTrimmed()], department: [education?.department ?? ''], degree: [education?.degree ?? ''], - startDate: [education?.startDate ?? null], - endDate: [education?.endDate ?? null], - ongoing: [education?.ongoing ?? false], + startDate: [education?.startDate ?? null, Validators.required], + endDate: [education?.endDate ?? null, endDateValidators], + ongoing: [isOngoing], }, - { validators: CustomValidators.dateRangeValidator } + { validators: CustomValidators.monthYearRangeValidator } ); } diff --git a/src/app/features/settings/profile-settings/components/employment-form/employment-form.component.html b/src/app/features/settings/profile-settings/components/employment-form/employment-form.component.html index 59d623cc7..b6c8b7394 100644 --- a/src/app/features/settings/profile-settings/components/employment-form/employment-form.component.html +++ b/src/app/features/settings/profile-settings/components/employment-form/employment-form.component.html @@ -9,7 +9,7 @@

[label]="'common.buttons.remove' | translate" severity="danger" variant="text" - (click)="remove.emit()" + (onClick)="remove.emit()" /> } @@ -33,11 +33,11 @@

- - - +
@@ -53,10 +53,17 @@

formControlName="startDate" [dateFormat]="dateFormat" [iconDisplay]="'input'" + view="month" [showIcon]="true" [maxDate]="maxDate" [minDate]="minDate" > + + @if (startDateRequiredError) { + + {{ 'validation.required' | translate }} + + } @if (!group().controls['ongoing'].value) { @@ -70,10 +77,17 @@

formControlName="endDate" [dateFormat]="dateFormat" [iconDisplay]="'input'" + view="month" [showIcon]="true" [maxDate]="maxDate" [minDate]="minDate" > + + @if (endDateRequiredError) { + + {{ 'validation.required' | translate }} + + } } @@ -93,7 +107,7 @@

@if (isDateError) { - + {{ 'settings.profileSettings.endDateError' | translate }} } diff --git a/src/app/features/settings/profile-settings/components/employment-form/employment-form.component.ts b/src/app/features/settings/profile-settings/components/employment-form/employment-form.component.ts index da5d0bbe9..d441bcba9 100644 --- a/src/app/features/settings/profile-settings/components/employment-form/employment-form.component.ts +++ b/src/app/features/settings/profile-settings/components/employment-form/employment-form.component.ts @@ -6,16 +6,15 @@ import { DatePicker } from 'primeng/datepicker'; import { InputText } from 'primeng/inputtext'; import { Message } from 'primeng/message'; -import { filter } from 'rxjs'; - import { ChangeDetectionStrategy, Component, DestroyRef, inject, input, OnInit, output } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; +import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; -import { MAX_DATE, MIN_DATE } from '@osf/features/settings/profile-settings/constants'; import { TextInputComponent } from '@osf/shared/components'; import { InputLimits } from '@osf/shared/constants'; +import { MAX_DATE, MIN_DATE } from '../../constants'; + @Component({ selector: 'osf-employment-form', imports: [ReactiveFormsModule, Button, InputText, DatePicker, Checkbox, Message, TranslatePipe, TextInputComponent], @@ -27,7 +26,7 @@ export class EmploymentFormComponent implements OnInit { readonly maxDate = MAX_DATE; readonly minDate = MIN_DATE; readonly institutionMaxLength = InputLimits.fullName.maxLength; - readonly dateFormat = 'mm/dd/yy'; + readonly dateFormat = 'mm/yy'; private readonly destroyRef = inject(DestroyRef); @@ -40,17 +39,39 @@ export class EmploymentFormComponent implements OnInit { return this.group().controls['title'] as FormControl; } + get institutionControl() { + return this.group().controls['institution'] as FormControl; + } + get isDateError() { const form = this.group(); return form.errors && form.errors['dateRangeInvalid']; } + get startDateRequiredError() { + const control = this.group().controls['startDate']; + return control.invalid && control.errors?.['required'] && (control.touched || control.dirty); + } + + get endDateRequiredError() { + const control = this.group().controls['endDate']; + return control.invalid && control.errors?.['required'] && (control.touched || control.dirty); + } + ngOnInit() { this.group() - .controls['ongoing'].valueChanges.pipe( - filter((res) => !!res), - takeUntilDestroyed(this.destroyRef) - ) - .subscribe(() => this.group().controls['endDate'].setValue(null)); + .controls['ongoing'].valueChanges.pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe((ongoing) => { + const endDateControl = this.group().controls['endDate']; + + if (ongoing) { + endDateControl.setValue(null); + endDateControl.clearValidators(); + } else { + endDateControl.setValidators([Validators.required]); + } + + endDateControl.updateValueAndValidity(); + }); } } diff --git a/src/app/features/settings/profile-settings/components/employment/employment.component.spec.ts b/src/app/features/settings/profile-settings/components/employment/employment.component.spec.ts index 0ea72671f..b319d0dc6 100644 --- a/src/app/features/settings/profile-settings/components/employment/employment.component.spec.ts +++ b/src/app/features/settings/profile-settings/components/employment/employment.component.spec.ts @@ -124,21 +124,19 @@ describe('EmploymentComponent', () => { component.saveEmployment(); expect(mockStore.dispatch).toHaveBeenCalledWith( - new UpdateProfileSettingsEmployment({ - employment: [ - { - title: 'Software Engineer Intern', - institution: 'Test University', - department: 'Engineering', - startYear: 2020, - startMonth: 1, - endYear: 2024, - endMonth: 6, - ongoing: false, - }, - expect.any(Object), - ], - }) + new UpdateProfileSettingsEmployment([ + { + title: 'Software Engineer Intern', + institution: 'Test University', + department: 'Engineering', + startYear: 2020, + startMonth: 1, + endYear: 2024, + endMonth: 6, + ongoing: false, + }, + expect.any(Object), + ]) ); }); }); diff --git a/src/app/features/settings/profile-settings/components/employment/employment.component.ts b/src/app/features/settings/profile-settings/components/employment/employment.component.ts index bfb0c7f3d..cd5b7b08e 100644 --- a/src/app/features/settings/profile-settings/components/employment/employment.component.ts +++ b/src/app/features/settings/profile-settings/components/employment/employment.component.ts @@ -14,7 +14,7 @@ import { inject, } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { FormArray, FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms'; +import { FormArray, FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; import { UpdateProfileSettingsEmployment, UserSelectors } from '@osf/core/store/user'; import { CustomValidators } from '@osf/shared/helpers'; @@ -97,7 +97,7 @@ export class EmploymentComponent { this.loaderService.show(); this.actions - .updateProfileSettingsEmployment({ employment: formattedEmployment }) + .updateProfileSettingsEmployment(formattedEmployment) .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe({ next: () => { @@ -137,16 +137,19 @@ export class EmploymentComponent { } private createEmploymentFormGroup(employment?: Partial): FormGroup { + const isOngoing = employment?.ongoing ?? false; + const endDateValidators = isOngoing ? [] : [Validators.required]; + return this.fb.group( { title: [employment?.title ?? '', CustomValidators.requiredTrimmed()], - institution: [employment?.institution ?? ''], + institution: [employment?.institution ?? '', CustomValidators.requiredTrimmed()], department: [employment?.department ?? ''], - startDate: [employment?.startDate ?? null], - endDate: [employment?.endDate ?? null], + startDate: [employment?.startDate ?? null, Validators.required], + endDate: [employment?.endDate ?? null, endDateValidators], ongoing: [employment?.ongoing ?? false], }, - { validators: CustomValidators.dateRangeValidator } + { validators: CustomValidators.monthYearRangeValidator } ); } diff --git a/src/app/features/settings/profile-settings/components/name/name.component.spec.ts b/src/app/features/settings/profile-settings/components/name/name.component.spec.ts index 1bc1eb5b6..778c0eedd 100644 --- a/src/app/features/settings/profile-settings/components/name/name.component.spec.ts +++ b/src/app/features/settings/profile-settings/components/name/name.component.spec.ts @@ -82,11 +82,7 @@ describe('NameComponent', () => { component.saveChanges(); - expect(mockStore.dispatch).toHaveBeenCalledWith( - new UpdateProfileSettingsUser({ - user: formData, - }) - ); + expect(mockStore.dispatch).toHaveBeenCalledWith(new UpdateProfileSettingsUser(formData)); }); it('should reset form to current user data', () => { diff --git a/src/app/features/settings/profile-settings/components/name/name.component.ts b/src/app/features/settings/profile-settings/components/name/name.component.ts index 2206d7b71..5d75a5d01 100644 --- a/src/app/features/settings/profile-settings/components/name/name.component.ts +++ b/src/app/features/settings/profile-settings/components/name/name.component.ts @@ -72,13 +72,11 @@ export class NameComponent { this.loaderService.show(); this.actions .updateProfileSettingsUser({ - user: { - fullName, - givenName, - middleNames, - familyName, - suffix, - }, + fullName, + givenName, + middleNames, + familyName, + suffix, }) .subscribe(() => { this.loaderService.hide(); diff --git a/src/app/features/settings/profile-settings/components/social/social.component.ts b/src/app/features/settings/profile-settings/components/social/social.component.ts index cd023b17b..b98815a4e 100644 --- a/src/app/features/settings/profile-settings/components/social/social.component.ts +++ b/src/app/features/settings/profile-settings/components/social/social.component.ts @@ -84,7 +84,7 @@ export class SocialComponent { this.loaderService.show(); this.actions - .updateProfileSettingsSocialLinks({ socialLinks: mappedLinks }) + .updateProfileSettingsSocialLinks(mappedLinks) .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe({ next: () => { diff --git a/src/app/features/settings/profile-settings/helpers/education-comparison.helper.ts b/src/app/features/settings/profile-settings/helpers/education-comparison.helper.ts index c87afdfec..cd14eced9 100644 --- a/src/app/features/settings/profile-settings/helpers/education-comparison.helper.ts +++ b/src/app/features/settings/profile-settings/helpers/education-comparison.helper.ts @@ -9,7 +9,7 @@ export function mapFormToEducation(education: EducationForm): Education { department: education.department, degree: education.degree, startYear: education.startDate?.getFullYear() ?? null, - startMonth: education.startDate?.getMonth() ?? null, + startMonth: education.startDate?.getMonth() + 1, endYear: education.ongoing ? null : (education.endDate?.getFullYear() ?? null), endMonth: education.ongoing ? null : education.endDate ? education.endDate.getMonth() + 1 : null, ongoing: education.ongoing, diff --git a/src/app/features/settings/profile-settings/helpers/employment-comparison.helper.ts b/src/app/features/settings/profile-settings/helpers/employment-comparison.helper.ts index 4f0c1cc68..26df69c78 100644 --- a/src/app/features/settings/profile-settings/helpers/employment-comparison.helper.ts +++ b/src/app/features/settings/profile-settings/helpers/employment-comparison.helper.ts @@ -9,7 +9,7 @@ export function mapFormToEmployment(employment: EmploymentForm): Employment { department: employment.department, institution: employment.institution, startYear: employment.startDate?.getFullYear() ?? new Date().getFullYear(), - startMonth: (employment.startDate?.getMonth() ?? 0) + 1, + startMonth: employment.startDate?.getMonth() + 1, endYear: employment.ongoing ? null : (employment.endDate?.getFullYear() ?? null), endMonth: employment.ongoing ? null : employment.endDate ? employment.endDate.getMonth() + 1 : null, ongoing: employment.ongoing, diff --git a/src/app/features/settings/profile-settings/profile-settings.component.ts b/src/app/features/settings/profile-settings/profile-settings.component.ts index 8e8cd3c8e..c61b6c068 100644 --- a/src/app/features/settings/profile-settings/profile-settings.component.ts +++ b/src/app/features/settings/profile-settings/profile-settings.component.ts @@ -36,11 +36,11 @@ import { ProfileSettingsTabOption } from './enums'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class ProfileSettingsComponent { - protected readonly isMedium = toSignal(inject(IS_MEDIUM)); - protected readonly tabOptions = PROFILE_SETTINGS_TAB_OPTIONS; - protected readonly tabOption = ProfileSettingsTabOption; + readonly isMedium = toSignal(inject(IS_MEDIUM)); + readonly tabOptions = PROFILE_SETTINGS_TAB_OPTIONS; + readonly tabOption = ProfileSettingsTabOption; - protected selectedTab = this.tabOption.Name; + selectedTab = this.tabOption.Name; onTabChange(index: number): void { this.selectedTab = index; diff --git a/src/app/shared/components/info-icon/info-icon.component.html b/src/app/shared/components/info-icon/info-icon.component.html index 698078345..991deeb72 100644 --- a/src/app/shared/components/info-icon/info-icon.component.html +++ b/src/app/shared/components/info-icon/info-icon.component.html @@ -1,5 +1,5 @@ diff --git a/src/app/shared/components/info-icon/info-icon.component.spec.ts b/src/app/shared/components/info-icon/info-icon.component.spec.ts index 120a5b814..e82e56d95 100644 --- a/src/app/shared/components/info-icon/info-icon.component.spec.ts +++ b/src/app/shared/components/info-icon/info-icon.component.spec.ts @@ -1,3 +1,6 @@ +import { TranslatePipe } from '@ngx-translate/core'; +import { MockPipe } from 'ng-mocks'; + import { ComponentRef } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; @@ -13,7 +16,7 @@ describe('InfoIconComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [InfoIconComponent], + imports: [InfoIconComponent, MockPipe(TranslatePipe)], }).compileComponents(); fixture = TestBed.createComponent(InfoIconComponent); diff --git a/src/app/shared/components/info-icon/info-icon.component.ts b/src/app/shared/components/info-icon/info-icon.component.ts index c663489cb..2db436fd7 100644 --- a/src/app/shared/components/info-icon/info-icon.component.ts +++ b/src/app/shared/components/info-icon/info-icon.component.ts @@ -1,3 +1,5 @@ +import { TranslatePipe } from '@ngx-translate/core'; + import { Tooltip } from 'primeng/tooltip'; import { ChangeDetectionStrategy, Component, input } from '@angular/core'; @@ -6,7 +8,7 @@ import { TooltipPosition } from '@osf/shared/models'; @Component({ selector: 'osf-info-icon', - imports: [Tooltip], + imports: [Tooltip, TranslatePipe], templateUrl: './info-icon.component.html', styleUrl: './info-icon.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/src/app/shared/helpers/custom-form-validators.helper.ts b/src/app/shared/helpers/custom-form-validators.helper.ts index 4fd1fc432..1b66206e3 100644 --- a/src/app/shared/helpers/custom-form-validators.helper.ts +++ b/src/app/shared/helpers/custom-form-validators.helper.ts @@ -51,7 +51,7 @@ export class CustomValidators { }; } - static dateRangeValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { + static monthYearRangeValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { const start = control.get('startDate')?.value; const end = control.get('endDate')?.value; @@ -60,7 +60,10 @@ export class CustomValidators { const startDate = new Date(start); const endDate = new Date(end); - return endDate > startDate ? null : { dateRangeInvalid: true }; + const startYearMonth = startDate.getFullYear() * 12 + startDate.getMonth(); + const endYearMonth = endDate.getFullYear() * 12 + endDate.getMonth(); + + return endYearMonth > startYearMonth ? null : { dateRangeInvalid: true }; }; static doiValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { diff --git a/src/app/shared/mocks/education.mock.ts b/src/app/shared/mocks/education.mock.ts index 7eca317ca..23400c19c 100644 --- a/src/app/shared/mocks/education.mock.ts +++ b/src/app/shared/mocks/education.mock.ts @@ -17,8 +17,8 @@ export const MOCK_EDUCATION: Education[] = [ degree: 'Master of Science', startMonth: 9, startYear: 2020, - endMonth: null, - endYear: null, + endMonth: 8, + endYear: 2025, ongoing: false, }, ];