69432: Profile metadata form validation + notifications

This commit is contained in:
Kristof De Langhe
2020-03-11 11:54:32 +01:00
parent 53c457689c
commit 004297fcfa
5 changed files with 61 additions and 14 deletions

View File

@@ -1353,6 +1353,8 @@
"nav.mydspace": "MyDSpace", "nav.mydspace": "MyDSpace",
"nav.profile": "Profile",
"nav.search": "Search", "nav.search": "Search",
"nav.statistics.header": "Statistics", "nav.statistics.header": "Statistics",
@@ -1421,9 +1423,9 @@
"profile.head": "Update Profile", "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", "profile.metadata.form.label.email": "Email Address",
@@ -1435,6 +1437,10 @@
"profile.metadata.form.label.phone": "Contact Telephone", "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.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", "profile.security.form.label.password": "Password",

View File

@@ -37,6 +37,12 @@ export function getAdminModulePath() {
return `/${ADMIN_MODULE_PATH}`; return `/${ADMIN_MODULE_PATH}`;
} }
const PROFILE_MODULE_PATH = 'profile';
export function getProfileModulePath() {
return `/${PROFILE_MODULE_PATH}`;
}
export function getDSOPath(dso: DSpaceObject): string { export function getDSOPath(dso: DSpaceObject): string {
switch ((dso as any).type) { switch ((dso as any).type) {
case Community.type.value: case Community.type.value:
@@ -68,7 +74,7 @@ export function getDSOPath(dso: DSpaceObject): string {
{ path: 'submit', loadChildren: './+submit-page/submit-page.module#SubmitPageModule' }, { path: 'submit', loadChildren: './+submit-page/submit-page.module#SubmitPageModule' },
{ path: 'workspaceitems', loadChildren: './+workspaceitems-edit-page/workspaceitems-edit-page.module#WorkspaceitemsEditPageModule' }, { path: 'workspaceitems', loadChildren: './+workspaceitems-edit-page/workspaceitems-edit-page.module#WorkspaceitemsEditPageModule' },
{ path: 'workflowitems', loadChildren: './+workflowitems-edit-page/workflowitems-edit-page.module#WorkflowItemsEditPageModule' }, { 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 }, { path: '**', pathMatch: 'full', component: PageNotFoundComponent },
]) ])
], ],

View File

@@ -1,4 +1,4 @@
import { Component, Inject, Input, OnInit } from '@angular/core'; import { Component, Inject, Input, OnInit, ViewChild } from '@angular/core';
import { import {
DynamicFormControlModel, DynamicFormControlModel,
DynamicFormService, DynamicFormValueControlModel, DynamicFormService, DynamicFormValueControlModel,
@@ -14,6 +14,10 @@ import { LangConfig } from '../../../config/lang-config.interface';
import { EPersonDataService } from '../../core/eperson/eperson-data.service'; import { EPersonDataService } from '../../core/eperson/eperson-data.service';
import { cloneDeep } from 'lodash'; import { cloneDeep } from 'lodash';
import { getRemoteDataPayload, getSucceededRemoteData } from '../../core/shared/operators'; 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({ @Component({
selector: 'ds-profile-page-metadata-form', selector: 'ds-profile-page-metadata-form',
@@ -25,6 +29,12 @@ export class ProfilePageMetadataFormComponent implements OnInit {
*/ */
@Input() user: EPerson; @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[] = [ formModel: DynamicFormControlModel[] = [
new DynamicInputModel({ new DynamicInputModel({
id: 'email', id: 'email',
@@ -72,6 +82,8 @@ export class ProfilePageMetadataFormComponent implements OnInit {
ERROR_PREFIX = 'profile.metadata.form.error.'; ERROR_PREFIX = 'profile.metadata.form.error.';
NOTIFICATION_PREFIX = 'profile.metadata.form.notifications.';
/** /**
* All of the configured active languages * All of the configured active languages
*/ */
@@ -79,13 +91,24 @@ export class ProfilePageMetadataFormComponent implements OnInit {
constructor(@Inject(GLOBAL_CONFIG) protected config: GlobalConfig, constructor(@Inject(GLOBAL_CONFIG) protected config: GlobalConfig,
protected location: Location, protected location: Location,
protected formService: DynamicFormService, protected formService: FormService,
protected formBuilderService: FormBuilderService,
protected translate: TranslateService, protected translate: TranslateService,
protected epersonService: EPersonDataService) { protected epersonService: EPersonDataService,
protected notificationsService: NotificationsService) {
} }
ngOnInit(): void { ngOnInit(): void {
this.activeLangs = this.config.languages.filter((MyLangConfig) => MyLangConfig.active === true); this.activeLangs = this.config.languages.filter((MyLangConfig) => MyLangConfig.active === true);
this.setFormValues();
this.updateFieldTranslations();
this.translate.onLangChange
.subscribe(() => {
this.updateFieldTranslations();
});
}
setFormValues() {
this.formModel.forEach( this.formModel.forEach(
(fieldModel: DynamicInputModel | DynamicSelectModel<string>) => { (fieldModel: DynamicInputModel | DynamicSelectModel<string>) => {
if (fieldModel.name === 'email') { if (fieldModel.name === 'email') {
@@ -99,12 +122,7 @@ export class ProfilePageMetadataFormComponent implements OnInit {
} }
} }
); );
this.formGroup = this.formService.createFormGroup(this.formModel); this.formGroup = this.formBuilderService.createFormGroup(this.formModel);
this.updateFieldTranslations();
this.translate.onLangChange
.subscribe(() => {
this.updateFieldTranslations();
});
} }
updateFieldTranslations() { updateFieldTranslations() {
@@ -122,8 +140,13 @@ export class ProfilePageMetadataFormComponent implements OnInit {
} }
updateProfile() { updateProfile() {
const newMetadata = Object.assign({}, this.user.metadata); if (!this.formRef.formGroup.valid) {
this.formModel.forEach((fieldModel: DynamicFormValueControlModel<string>) => { this.formService.validateAllFormFields(this.formGroup);
return;
}
const newMetadata = cloneDeep(this.user.metadata);
this.formModel.filter((fieldModel) => fieldModel.id !== 'email').forEach((fieldModel: DynamicFormValueControlModel<string>) => {
if (newMetadata.hasOwnProperty(fieldModel.name) && newMetadata[fieldModel.name].length > 0) { if (newMetadata.hasOwnProperty(fieldModel.name) && newMetadata[fieldModel.name].length > 0) {
if (hasValue(fieldModel.value)) { if (hasValue(fieldModel.value)) {
newMetadata[fieldModel.name][0].value = fieldModel.value; newMetadata[fieldModel.name][0].value = fieldModel.value;
@@ -142,6 +165,11 @@ export class ProfilePageMetadataFormComponent implements OnInit {
getRemoteDataPayload() getRemoteDataPayload()
).subscribe((user) => { ).subscribe((user) => {
this.user = 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')
);
}); });
} }
} }

View File

@@ -1,6 +1,7 @@
<ds-loading *ngIf="(loading$ | async)"></ds-loading> <ds-loading *ngIf="(loading$ | async)"></ds-loading>
<div *ngIf="!(loading$ | async)"> <div *ngIf="!(loading$ | async)">
<span class="dropdown-item-text">{{(user$ | async)?.name}} ({{(user$ | async)?.email}})</span> <span class="dropdown-item-text">{{(user$ | async)?.name}} ({{(user$ | async)?.email}})</span>
<a class="dropdown-item" [routerLink]="[profileRoute]" routerLinkActive="active">{{'nav.profile' | translate}}</a>
<a class="dropdown-item" [routerLink]="[mydspaceRoute]" routerLinkActive="active">{{'nav.mydspace' | translate}}</a> <a class="dropdown-item" [routerLink]="[mydspaceRoute]" routerLinkActive="active">{{'nav.mydspace' | translate}}</a>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<ds-log-out></ds-log-out> <ds-log-out></ds-log-out>

View File

@@ -7,6 +7,7 @@ import { EPerson } from '../../../core/eperson/models/eperson.model';
import { AppState } from '../../../app.reducer'; import { AppState } from '../../../app.reducer';
import { getAuthenticatedUser, isAuthenticationLoading } from '../../../core/auth/selectors'; import { getAuthenticatedUser, isAuthenticationLoading } from '../../../core/auth/selectors';
import { MYDSPACE_ROUTE } from '../../../+my-dspace-page/my-dspace-page.component'; import { MYDSPACE_ROUTE } from '../../../+my-dspace-page/my-dspace-page.component';
import { getProfileModulePath } from '../../../app-routing.module';
/** /**
* This component represents the user nav menu. * This component represents the user nav menu.
@@ -36,6 +37,11 @@ export class UserMenuComponent implements OnInit {
*/ */
public mydspaceRoute = MYDSPACE_ROUTE; public mydspaceRoute = MYDSPACE_ROUTE;
/**
* The profile page route
*/
public profileRoute = getProfileModulePath();
constructor(private store: Store<AppState>) { constructor(private store: Store<AppState>) {
} }