diff --git a/resources/i18n/en.json5 b/resources/i18n/en.json5 index 82f295f28e..a9cee1bd6f 100644 --- a/resources/i18n/en.json5 +++ b/resources/i18n/en.json5 @@ -1353,6 +1353,8 @@ "nav.mydspace": "MyDSpace", + "nav.profile": "Profile", + "nav.search": "Search", "nav.statistics.header": "Statistics", @@ -1421,9 +1423,9 @@ "profile.head": "Update Profile", - "profile.metadata.form.error.firstname": "First Name is required", + "profile.metadata.form.error.firstname.required": "First Name is required", - "profile.metadata.form.error.lastname": "Last Name is required", + "profile.metadata.form.error.lastname.required": "Last Name is required", "profile.metadata.form.label.email": "Email Address", @@ -1435,6 +1437,10 @@ "profile.metadata.form.label.phone": "Contact Telephone", + "profile.metadata.form.notifications.success.content": "Your changes to the profile were saved.", + + "profile.metadata.form.notifications.success.title": "Profile saved", + "profile.security.form.info": "Optionally, you can enter a new password in the box below, and confirm it by typing it again into the second box. It should be at least six characters long.", "profile.security.form.label.password": "Password", diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index da2b896241..a2c3713861 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -37,6 +37,12 @@ export function getAdminModulePath() { return `/${ADMIN_MODULE_PATH}`; } +const PROFILE_MODULE_PATH = 'profile'; + +export function getProfileModulePath() { + return `/${PROFILE_MODULE_PATH}`; +} + export function getDSOPath(dso: DSpaceObject): string { switch ((dso as any).type) { case Community.type.value: @@ -68,7 +74,7 @@ export function getDSOPath(dso: DSpaceObject): string { { path: 'submit', loadChildren: './+submit-page/submit-page.module#SubmitPageModule' }, { path: 'workspaceitems', loadChildren: './+workspaceitems-edit-page/workspaceitems-edit-page.module#WorkspaceitemsEditPageModule' }, { path: 'workflowitems', loadChildren: './+workflowitems-edit-page/workflowitems-edit-page.module#WorkflowItemsEditPageModule' }, - { path: 'profile', loadChildren: './profile-page/profile-page.module#ProfilePageModule', canActivate: [AuthenticatedGuard] }, + { path: PROFILE_MODULE_PATH, loadChildren: './profile-page/profile-page.module#ProfilePageModule', canActivate: [AuthenticatedGuard] }, { path: '**', pathMatch: 'full', component: PageNotFoundComponent }, ]) ], diff --git a/src/app/profile-page/profile-page-metadata-form/profile-page-metadata-form.component.ts b/src/app/profile-page/profile-page-metadata-form/profile-page-metadata-form.component.ts index 6c5b9acb55..cf59f2d331 100644 --- a/src/app/profile-page/profile-page-metadata-form/profile-page-metadata-form.component.ts +++ b/src/app/profile-page/profile-page-metadata-form/profile-page-metadata-form.component.ts @@ -1,4 +1,4 @@ -import { Component, Inject, Input, OnInit } from '@angular/core'; +import { Component, Inject, Input, OnInit, ViewChild } from '@angular/core'; import { DynamicFormControlModel, DynamicFormService, DynamicFormValueControlModel, @@ -14,6 +14,10 @@ import { LangConfig } from '../../../config/lang-config.interface'; import { EPersonDataService } from '../../core/eperson/eperson-data.service'; import { cloneDeep } from 'lodash'; import { getRemoteDataPayload, getSucceededRemoteData } from '../../core/shared/operators'; +import { FormService } from '../../shared/form/form.service'; +import { FormBuilderService } from '../../shared/form/builder/form-builder.service'; +import { FormComponent } from '../../shared/form/form.component'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; @Component({ selector: 'ds-profile-page-metadata-form', @@ -25,6 +29,12 @@ export class ProfilePageMetadataFormComponent implements OnInit { */ @Input() user: EPerson; + /** + * Reference to the form component + * Used for validating the form before sending update requests + */ + @ViewChild(FormComponent, { static: false }) formRef: FormComponent; + formModel: DynamicFormControlModel[] = [ new DynamicInputModel({ id: 'email', @@ -72,6 +82,8 @@ export class ProfilePageMetadataFormComponent implements OnInit { ERROR_PREFIX = 'profile.metadata.form.error.'; + NOTIFICATION_PREFIX = 'profile.metadata.form.notifications.'; + /** * All of the configured active languages */ @@ -79,13 +91,24 @@ export class ProfilePageMetadataFormComponent implements OnInit { constructor(@Inject(GLOBAL_CONFIG) protected config: GlobalConfig, protected location: Location, - protected formService: DynamicFormService, + protected formService: FormService, + protected formBuilderService: FormBuilderService, protected translate: TranslateService, - protected epersonService: EPersonDataService) { + protected epersonService: EPersonDataService, + protected notificationsService: NotificationsService) { } ngOnInit(): void { this.activeLangs = this.config.languages.filter((MyLangConfig) => MyLangConfig.active === true); + this.setFormValues(); + this.updateFieldTranslations(); + this.translate.onLangChange + .subscribe(() => { + this.updateFieldTranslations(); + }); + } + + setFormValues() { this.formModel.forEach( (fieldModel: DynamicInputModel | DynamicSelectModel) => { if (fieldModel.name === 'email') { @@ -99,12 +122,7 @@ export class ProfilePageMetadataFormComponent implements OnInit { } } ); - this.formGroup = this.formService.createFormGroup(this.formModel); - this.updateFieldTranslations(); - this.translate.onLangChange - .subscribe(() => { - this.updateFieldTranslations(); - }); + this.formGroup = this.formBuilderService.createFormGroup(this.formModel); } updateFieldTranslations() { @@ -122,8 +140,13 @@ export class ProfilePageMetadataFormComponent implements OnInit { } updateProfile() { - const newMetadata = Object.assign({}, this.user.metadata); - this.formModel.forEach((fieldModel: DynamicFormValueControlModel) => { + if (!this.formRef.formGroup.valid) { + this.formService.validateAllFormFields(this.formGroup); + return; + } + + const newMetadata = cloneDeep(this.user.metadata); + this.formModel.filter((fieldModel) => fieldModel.id !== 'email').forEach((fieldModel: DynamicFormValueControlModel) => { if (newMetadata.hasOwnProperty(fieldModel.name) && newMetadata[fieldModel.name].length > 0) { if (hasValue(fieldModel.value)) { newMetadata[fieldModel.name][0].value = fieldModel.value; @@ -142,6 +165,11 @@ export class ProfilePageMetadataFormComponent implements OnInit { getRemoteDataPayload() ).subscribe((user) => { this.user = user; + this.setFormValues(); + this.notificationsService.success( + this.translate.instant(this.NOTIFICATION_PREFIX + 'success.title'), + this.translate.instant(this.NOTIFICATION_PREFIX + 'success.content') + ); }); } } diff --git a/src/app/shared/auth-nav-menu/user-menu/user-menu.component.html b/src/app/shared/auth-nav-menu/user-menu/user-menu.component.html index fef47b395b..ac55a211e9 100644 --- a/src/app/shared/auth-nav-menu/user-menu/user-menu.component.html +++ b/src/app/shared/auth-nav-menu/user-menu/user-menu.component.html @@ -1,6 +1,7 @@
{{(user$ | async)?.name}} ({{(user$ | async)?.email}}) + {{'nav.profile' | translate}} {{'nav.mydspace' | translate}} diff --git a/src/app/shared/auth-nav-menu/user-menu/user-menu.component.ts b/src/app/shared/auth-nav-menu/user-menu/user-menu.component.ts index e3c21b4e24..2d57a837c7 100644 --- a/src/app/shared/auth-nav-menu/user-menu/user-menu.component.ts +++ b/src/app/shared/auth-nav-menu/user-menu/user-menu.component.ts @@ -7,6 +7,7 @@ import { EPerson } from '../../../core/eperson/models/eperson.model'; import { AppState } from '../../../app.reducer'; import { getAuthenticatedUser, isAuthenticationLoading } from '../../../core/auth/selectors'; import { MYDSPACE_ROUTE } from '../../../+my-dspace-page/my-dspace-page.component'; +import { getProfileModulePath } from '../../../app-routing.module'; /** * This component represents the user nav menu. @@ -36,6 +37,11 @@ export class UserMenuComponent implements OnInit { */ public mydspaceRoute = MYDSPACE_ROUTE; + /** + * The profile page route + */ + public profileRoute = getProfileModulePath(); + constructor(private store: Store) { }