Merge pull request #4316 from tsiegleauq/remove-ngsubmit

Remove redundant submits
This commit is contained in:
Emanuel Schütze 2019-02-14 15:06:28 +01:00 committed by GitHub
commit aaa9157873
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 193 additions and 165 deletions

View File

@ -82,7 +82,6 @@ export class ItemRepositoryService extends BaseRepository<ViewItem, Item> {
/** /**
* Returns the corresponding content object to a given {@link Item} as an {@link AgendaBaseViewModel} * Returns the corresponding content object to a given {@link Item} as an {@link AgendaBaseViewModel}
* Used dynamically because of heavy race conditions
* *
* @param agendaItem the target agenda Item * @param agendaItem the target agenda Item
* @returns the content object of the given item. Might be null if it was not found. * @returns the content object of the given item. Might be null if it was not found.

View File

@ -111,14 +111,13 @@ export class AgendaListComponent extends ListViewBaseComponent<ViewItem> impleme
/** /**
* Links to the content object. * Links to the content object.
* Gets content object from the repository rather than from the model
* to avoid race conditions
* *
* @param item the item that was selected from the list view * @param item the item that was selected from the list view
*/ */
public singleSelectAction(item: ViewItem): void { public singleSelectAction(item: ViewItem): void {
const contentObject = this.repo.getContentObject(item.item); if (item.contentObject) {
this.router.navigate([contentObject.getDetailStateURL()]); this.router.navigate([item.contentObject.getDetailStateURL()]);
}
} }
/** /**

View File

@ -1,6 +1,6 @@
<h1 mat-dialog-title>{{ 'Edit details for' | translate }} {{ item.getTitle() }}</h1> <h1 mat-dialog-title>{{ 'Edit details for' | translate }} {{ item.getTitle() }}</h1>
<div mat-dialog-content> <div mat-dialog-content>
<form class="itemDialogForm" [formGroup]="agendaInfoForm" (keydown)="onKeyDown($event)" (ngSubmit)="saveItemInfo()"> <form class="itemDialogForm" [formGroup]="agendaInfoForm" (keydown)="onKeyDown($event)">
<!-- Visibility --> <!-- Visibility -->
<mat-form-field> <mat-form-field>
<mat-select formControlName="type" placeholder="{{ 'Agenda visibility' | translate }}"> <mat-select formControlName="type" placeholder="{{ 'Agenda visibility' | translate }}">

View File

@ -10,7 +10,7 @@
<!-- Title --> <!-- Title -->
<div class="title-slot"> <div class="title-slot">
<h2> <h2>
<span *ngIf="newTopic " translate>New topic</span> <span *ngIf="newTopic" translate>New topic</span>
<span *ngIf="editTopic && !newTopic" translate>Edit topic</span> <span *ngIf="editTopic && !newTopic" translate>Edit topic</span>
<span *ngIf="!newTopic && !editTopic" translate>Topic</span> <span *ngIf="!newTopic && !editTopic" translate>Topic</span>
</h2> </h2>
@ -24,9 +24,7 @@
</div> </div>
</os-head-bar> </os-head-bar>
<mat-card *ngIf="topic || editTopic" <mat-card *ngIf="topic || editTopic" [ngClass]="editTopic ? 'os-form-card' : 'os-card'" class="on-transition-fade">
[ngClass]="editTopic ? 'os-form-card' : 'os-card'"
class="on-transition-fade">
<div *ngIf="!editTopic"> <div *ngIf="!editTopic">
<h1>{{ topic.title }}</h1> <h1>{{ topic.title }}</h1>
</div> </div>
@ -49,7 +47,7 @@
</h3> </h3>
</div> </div>
<form *ngIf="editTopic" [formGroup]="topicForm" (keydown)="onKeyDown($event)" (ngSubmit)="saveTopic()"> <form *ngIf="editTopic" [formGroup]="topicForm" (keydown)="onKeyDown($event)">
<div> <div>
<mat-form-field> <mat-form-field>
<input <input

View File

@ -2,14 +2,28 @@
<form [formGroup]="newPasswordForm" (ngSubmit)="submitNewPassword()"> <form [formGroup]="newPasswordForm" (ngSubmit)="submitNewPassword()">
<h3 translate>Please enter your new password</h3> <h3 translate>Please enter your new password</h3>
<mat-form-field> <mat-form-field>
<input matInput required placeholder="{{ 'New password' | translate }}" formControlName="password" type="password"> <input
matInput
required
placeholder="{{ 'New password' | translate }}"
formControlName="password"
type="password"
/>
<mat-error *ngIf="newPasswordForm.get('password').hasError('required')" translate> <mat-error *ngIf="newPasswordForm.get('password').hasError('required')" translate>
A password is required A password is required
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<br> <br />
<button mat-raised-button color="primary" class="submit-button" [disabled]="newPasswordForm.invalid" <button
type="submit" translate>Reset password</button> type="submit"
<button mat-button class="back-button" routerLink="/login" translate>Back to login</button> mat-raised-button
color="primary"
class="submit-button"
[disabled]="newPasswordForm.invalid"
translate
>
Reset password
</button>
<button type="button" class="back-button" routerLink="/login" translate>Back to login</button>
</form> </form>
</div> </div>

View File

@ -2,14 +2,21 @@
<form [formGroup]="resetPasswordForm" (ngSubmit)="resetPassword()"> <form [formGroup]="resetPasswordForm" (ngSubmit)="resetPassword()">
<h3 translate>Enter your email to send the password reset link</h3> <h3 translate>Enter your email to send the password reset link</h3>
<mat-form-field> <mat-form-field>
<input matInput required placeholder="{{ 'Email' | translate }}" formControlName="email" type="email"> <input matInput required placeholder="{{ 'Email' | translate }}" formControlName="email" type="email" />
<mat-error *ngIf="resetPasswordForm.get('email').invalid" translate> <mat-error *ngIf="resetPasswordForm.get('email').invalid" translate>
Please enter a valid email address! Please enter a valid email address!
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<br> <br />
<button mat-raised-button color="primary" class="submit-button" [disabled]="resetPasswordForm.invalid" <button
type="submit">{{ 'Reset password' | translate }}</button>&nbsp; type="submit"
<button mat-button routerLink="/login">{{ 'Back' | translate }}</button> mat-raised-button
color="primary"
class="submit-button"
[disabled]="resetPasswordForm.invalid"
>
{{ 'Reset password' | translate }}</button
>&nbsp;
<button type="button" mat-button routerLink="/login">{{ 'Back' | translate }}</button>
</form> </form>
</div> </div>

View File

@ -3,7 +3,8 @@
[editMode]="editFile" [editMode]="editFile"
[multiSelectMode]="isMultiSelect" [multiSelectMode]="isMultiSelect"
(mainEvent)="onMainEvent()" (mainEvent)="onMainEvent()"
(saveEvent)="onSaveEditedFile()"> (saveEvent)="onSaveEditedFile()"
>
<!-- Title --> <!-- Title -->
<div class="title-slot"> <div class="title-slot">
<h2 *ngIf="!editFile" translate>Files</h2> <h2 *ngIf="!editFile" translate>Files</h2>
@ -13,7 +14,8 @@
*ngIf="editFile" *ngIf="editFile"
[formGroup]="fileEditForm" [formGroup]="fileEditForm"
(ngSubmit)="onSaveEditedFile()" (ngSubmit)="onSaveEditedFile()"
(keydown)="keyDownFunction($event)"> (keydown)="keyDownFunction($event)"
>
<mat-form-field> <mat-form-field>
<input <input
type="text" type="text"
@ -21,7 +23,8 @@
osAutofocus osAutofocus
required required
formControlName="title" formControlName="title"
placeholder="{{ 'New file name' | translate }}"/> placeholder="{{ 'New file name' | translate }}"
/>
<mat-error *ngIf="fileEditForm.invalid" translate>Required</mat-error> <mat-error *ngIf="fileEditForm.invalid" translate>Required</mat-error>
</mat-form-field> </mat-form-field>
@ -45,135 +48,143 @@
<button mat-icon-button (click)="toggleMultiSelect()"><mat-icon>arrow_back</mat-icon></button> <button mat-icon-button (click)="toggleMultiSelect()"><mat-icon>arrow_back</mat-icon></button>
<span>{{ selectedRows.length }}&nbsp;</span><span translate>selected</span> <span>{{ selectedRows.length }}&nbsp;</span><span translate>selected</span>
</div> </div>
</os-head-bar> </os-head-bar>
<mat-drawer-container class="on-transition-fade"> <mat-drawer-container class="on-transition-fade">
<os-sort-filter-bar [filterCount]="filteredCount" [sortService]="sortService" [filterService]="filterService" <os-sort-filter-bar
(searchFieldChange)="searchFilter($event)"> [filterCount]="filteredCount"
</os-sort-filter-bar> [sortService]="sortService"
<mat-table class="os-listview-table on-transition-fade" [dataSource]="dataSource" matSort> [filterService]="filterService"
<!-- Selector Column --> (searchFieldChange)="searchFilter($event)"
<ng-container matColumnDef="selector"> >
<mat-header-cell *matHeaderCellDef mat-sort-header class="icon-cell"></mat-header-cell> </os-sort-filter-bar>
<mat-cell *matCellDef="let item" class="icon-cell" (click)="selectItem(item, $event)"> <mat-table class="os-listview-table on-transition-fade" [dataSource]="dataSource" matSort>
<mat-icon>{{ isSelected(item) ? 'check_circle' : '' }}</mat-icon> <!-- Selector Column -->
</mat-cell> <ng-container matColumnDef="selector">
</ng-container> <mat-header-cell *matHeaderCellDef mat-sort-header class="icon-cell"></mat-header-cell>
<mat-cell *matCellDef="let item" class="icon-cell" (click)="selectItem(item, $event)">
<mat-icon>{{ isSelected(item) ? 'check_circle' : '' }}</mat-icon>
</mat-cell>
</ng-container>
<!-- Filename --> <!-- Filename -->
<ng-container matColumnDef="title"> <ng-container matColumnDef="title">
<mat-header-cell *matHeaderCellDef mat-sort-header>Name</mat-header-cell> <mat-header-cell *matHeaderCellDef mat-sort-header>Name</mat-header-cell>
<mat-cell *matCellDef="let file" (click)="selectItem(file, $event)">{{ file.title }}</mat-cell> <mat-cell *matCellDef="let file" (click)="selectItem(file, $event)">{{ file.title }}</mat-cell>
</ng-container> </ng-container>
<!-- Info --> <!-- Info -->
<ng-container matColumnDef="info"> <ng-container matColumnDef="info">
<mat-header-cell *matHeaderCellDef mat-sort-header>Group</mat-header-cell> <mat-header-cell *matHeaderCellDef mat-sort-header>Group</mat-header-cell>
<mat-cell *matCellDef="let file" (click)="selectItem(file, $event)"> <mat-cell *matCellDef="let file" (click)="selectItem(file, $event)">
<div class="file-info-cell"> <div class="file-info-cell">
<span> <mat-icon [inline]="true">insert_drive_file</mat-icon> {{ file.type }} </span> <span> <mat-icon [inline]="true">insert_drive_file</mat-icon> {{ file.type }} </span>
<span> <mat-icon [inline]="true">data_usage</mat-icon> {{ file.size }} </span> <span> <mat-icon [inline]="true">data_usage</mat-icon> {{ file.size }} </span>
</div>
</mat-cell>
</ng-container>
<!-- indicator -->
<ng-container matColumnDef="indicator">
<mat-header-cell *matHeaderCellDef mat-sort-header>Indicator</mat-header-cell>
<mat-cell *matCellDef="let file" (click)="selectItem(file, $event)">
<!-- check if the file is managed -->
<div
*ngIf="getFileSettings(file).length > 0"
[matMenuTriggerFor]="singleFileMenu"
[matMenuTriggerData]="{ file: file }"
[matTooltip]="formatIndicatorTooltip(file)"
>
<mat-icon *ngIf="file.isFont()">text_fields</mat-icon>
<mat-icon *ngIf="file.isImage()">insert_photo</mat-icon>
</div>
</mat-cell>
</ng-container>
<!-- menu -->
<ng-container matColumnDef="menu">
<mat-header-cell *matHeaderCellDef mat-sort-header>Menu</mat-header-cell>
<mat-cell *matCellDef="let file">
<button mat-icon-button [matMenuTriggerFor]="singleFileMenu" [matMenuTriggerData]="{ file: file }">
<mat-icon>more_vert</mat-icon>
</button>
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="getColumnDefinition()"></mat-header-row>
<mat-row
*matRowDef="let row; columns: getColumnDefinition()"
[ngClass]="selectedRows.indexOf(row) >= 0 ? 'selected' : ''"
></mat-row>
</mat-table>
<mat-paginator class="on-transition-fade" [pageSizeOptions]="[25, 50, 75, 100, 125]"></mat-paginator>
<mat-menu #singleFileMenu="matMenu">
<ng-template matMenuContent let-file="file">
<!-- Exclusive for images -->
<div *ngIf="file.isImage()">
<div *ngFor="let action of logoActions">
<ng-container
*ngTemplateOutlet="manageButton; context: { file: file, action: action }"
></ng-container>
</div>
</div> </div>
</mat-cell>
</ng-container>
<!-- indicator --> <!-- Exclusive for fonts -->
<ng-container matColumnDef="indicator"> <div *ngIf="file.isFont()">
<mat-header-cell *matHeaderCellDef mat-sort-header>Indicator</mat-header-cell> <div *ngFor="let action of fontActions">
<mat-cell *matCellDef="let file" (click)="selectItem(file, $event)"> <ng-container
<!-- check if the file is managed --> *ngTemplateOutlet="manageButton; context: { file: file, action: action }"
></ng-container>
<div </div>
*ngIf="getFileSettings(file).length > 0"
[matMenuTriggerFor]="singleFileMenu"
[matMenuTriggerData]="{ file: file }"
[matTooltip]="formatIndicatorTooltip(file)">
<mat-icon *ngIf="file.isFont()">text_fields</mat-icon>
<mat-icon *ngIf="file.isImage()">insert_photo</mat-icon>
</div> </div>
</mat-cell>
</ng-container>
<!-- menu --> <!-- Edit and delete for all images -->
<ng-container matColumnDef="menu"> <mat-divider></mat-divider>
<mat-header-cell *matHeaderCellDef mat-sort-header>Menu</mat-header-cell> <button mat-menu-item (click)="onEditFile(file)">
<mat-cell *matCellDef="let file"> <mat-icon>edit</mat-icon>
<button mat-icon-button [matMenuTriggerFor]="singleFileMenu" [matMenuTriggerData]="{ file: file }"> <span translate>Edit</span>
<mat-icon>more_vert</mat-icon>
</button> </button>
</mat-cell> <button mat-menu-item class="red-warning-text" (click)="onDelete(file)">
</ng-container> <mat-icon>delete</mat-icon>
<span translate>Delete</span>
</button>
</ng-template>
</mat-menu>
<mat-header-row *matHeaderRowDef="getColumnDefinition()"></mat-header-row> <!-- Template for the managing buttons -->
<mat-row <ng-template #manageButton let-file="file" let-action="action">
*matRowDef="let row; columns: getColumnDefinition()" <button mat-menu-item (click)="onManageButton($event, file, action)">
[ngClass]="selectedRows.indexOf(row) >= 0 ? 'selected' : ''" <mat-icon color="accent"> {{ isUsedAs(file, action) ? 'check_box' : 'check_box_outline_blank' }} </mat-icon>
></mat-row> <span>{{ getNameOfAction(action) }}</span>
</mat-table>
<mat-paginator class="on-transition-fade" [pageSizeOptions]="[25, 50, 75, 100, 125]"></mat-paginator>
<mat-menu #singleFileMenu="matMenu">
<ng-template matMenuContent let-file="file">
<!-- Exclusive for images -->
<div *ngIf="file.isImage()">
<div *ngFor="let action of logoActions">
<ng-container *ngTemplateOutlet="manageButton; context: { file: file, action: action }"></ng-container>
</div>
</div>
<!-- Exclusive for fonts -->
<div *ngIf="file.isFont()">
<div *ngFor="let action of fontActions">
<ng-container *ngTemplateOutlet="manageButton; context: { file: file, action: action }"></ng-container>
</div>
</div>
<!-- Edit and delete for all images -->
<mat-divider></mat-divider>
<button mat-menu-item (click)="onEditFile(file)">
<mat-icon>edit</mat-icon>
<span translate>Edit</span>
</button>
<button mat-menu-item class="red-warning-text" (click)="onDelete(file)">
<mat-icon>delete</mat-icon>
<span translate>Delete</span>
</button> </button>
</ng-template> </ng-template>
</mat-menu>
<!-- Template for the managing buttons --> <!-- Menu for Mediafiles -->
<ng-template #manageButton let-file="file" let-action="action"> <mat-menu #mediafilesMenu="matMenu">
<button mat-menu-item (click)="onManageButton($event, file, action)"> <div *ngIf="!isMultiSelect">
<mat-icon color="accent"> {{ isUsedAs(file, action) ? 'check_box' : 'check_box_outline_blank' }} </mat-icon> <button mat-menu-item *osPerms="'mediafiles.can_manage'" (click)="toggleMultiSelect()">
<span>{{ getNameOfAction(action) }}</span> <mat-icon>library_add</mat-icon>
</button> <span translate>Multiselect</span>
</ng-template> </button>
</div>
<!-- Menu for Mediafiles --> <div *ngIf="isMultiSelect">
<mat-menu #mediafilesMenu="matMenu"> <mat-divider></mat-divider>
<div *ngIf="!isMultiSelect"> <button mat-menu-item (click)="selectAll()">
<button mat-menu-item *osPerms="'mediafiles.can_manage'" (click)="toggleMultiSelect()"> <mat-icon>done_all</mat-icon>
<mat-icon>library_add</mat-icon> <span translate>Select all</span>
<span translate>Multiselect</span> </button>
</button> <button mat-menu-item (click)="deselectAll()">
</div> <mat-icon>clear</mat-icon>
<div *ngIf="isMultiSelect"> <span translate>Deselect all</span>
<mat-divider></mat-divider> </button>
<button mat-menu-item (click)="selectAll()"> <mat-divider></mat-divider>
<mat-icon>done_all</mat-icon> <button mat-menu-item *osPerms="'mediafiles.can_manage'" (click)="deleteSelected()">
<span translate>Select all</span> <mat-icon>delete</mat-icon>
</button> <span translate>Delete</span>
<button mat-menu-item (click)="deselectAll()"> </button>
<mat-icon>clear</mat-icon> </div>
<span translate>Deselect all</span> </mat-menu>
</button>
<mat-divider></mat-divider>
<button mat-menu-item *ngIf="canEdit" (click)="deleteSelected()">
<mat-icon>delete</mat-icon>
<span translate>Delete</span>
</button>
</div>
</mat-menu>
</mat-drawer-container> </mat-drawer-container>

View File

@ -26,7 +26,8 @@
type="button" type="button"
mat-button mat-button
matStepperNext matStepperNext
[disabled]="contentForm.value.selectedParagraph === null"> [disabled]="contentForm.value.selectedParagraph === null"
>
<span translate>Next</span> <span translate>Next</span>
</button> </button>
</div> </div>

View File

@ -420,13 +420,7 @@
</ng-template> </ng-template>
<ng-template #contentTemplate> <ng-template #contentTemplate>
<form <form class="motion-content" [formGroup]="contentForm" (keydown)="onKeyDown($event)" *ngIf="motion">
class="motion-content"
[formGroup]="contentForm"
(keydown)="onKeyDown($event)"
(ngSubmit)="saveMotion()"
*ngIf="motion"
>
<!-- Line Number and Diff buttons --> <!-- Line Number and Diff buttons -->
<div *ngIf="!editMotion && !motion.isStatuteAmendment()" class="motion-text-controls"> <div *ngIf="!editMotion && !motion.isStatuteAmendment()" class="motion-text-controls">
<mat-form-field class="motion-goto-line" *ngIf="highlightedLineOpened"> <mat-form-field class="motion-goto-line" *ngIf="highlightedLineOpened">

View File

@ -1,12 +1,24 @@
<os-head-bar [mainButton]="true" [nav]="true" [editMode]="editTag" <os-head-bar
(mainEvent)="setEditMode(!editTag)" (saveEvent)="saveTag()" [multiSelectMode]="isMultiSelect">> [mainButton]="true"
[nav]="true"
[editMode]="editTag"
(mainEvent)="setEditMode(!editTag)"
(saveEvent)="saveTag()"
[multiSelectMode]="isMultiSelect"
>
<!-- Title --> <!-- Title -->
<div class="title-slot"> <div class="title-slot">
<h2 *ngIf="!editTag && !newTag" translate>Tags</h2> <h2 *ngIf="!editTag && !newTag" translate>Tags</h2>
<form *ngIf="editTag" [formGroup]="tagForm" (ngSubmit)="saveTag()" (keydown)="keyDownFunction($event)"> <form *ngIf="editTag" [formGroup]="tagForm" (keydown)="keyDownFunction($event)">
<mat-form-field> <mat-form-field>
<input type="text" matInput osAutofocus required formControlName="name" <input
placeholder="{{ 'New tag name' | translate}}"> type="text"
matInput
osAutofocus
required
formControlName="name"
placeholder="{{ 'New tag name' | translate }}"
/>
<mat-error *ngIf="tagForm.invalid" translate>Required</mat-error> <mat-error *ngIf="tagForm.invalid" translate>Required</mat-error>
</mat-form-field> </mat-form-field>
</form> </form>
@ -14,12 +26,11 @@
<!-- remove button --> <!-- remove button -->
<div class="extra-controls-slot on-transition-fade"> <div class="extra-controls-slot on-transition-fade">
<button *ngIf="!isMultiSelect && editTag && !newTag" type="button" mat-button (click)="deleteSelectedTag()"> <button type="button" mat-button *ngIf="!isMultiSelect && editTag && !newTag" (click)="deleteSelectedTag()">
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>
<span translate>Delete</span> <span translate>Delete</span>
</button> </button>
</div> </div>
</os-head-bar> </os-head-bar>
<mat-table class="os-listview-table on-transition-fade" [dataSource]="dataSource" matSort> <mat-table class="os-listview-table on-transition-fade" [dataSource]="dataSource" matSort>
@ -28,7 +39,7 @@
<mat-cell *matCellDef="let tag">{{ tag.getTitle() }}</mat-cell> <mat-cell *matCellDef="let tag">{{ tag.getTitle() }}</mat-cell>
</ng-container> </ng-container>
<mat-header-row *matHeaderRowDef="['name']"></mat-header-row> <mat-header-row *matHeaderRowDef="['name']"></mat-header-row>
<mat-row (click)='selectItem(row, $event)' *matRowDef="let row; columns: ['name']"></mat-row> <mat-row (click)="selectItem(row, $event)" *matRowDef="let row; columns: ['name']"></mat-row>
</mat-table> </mat-table>
<mat-paginator class="on-transition-fade" [pageSizeOptions]="[25, 50, 75, 100, 125]"></mat-paginator> <mat-paginator class="on-transition-fade" [pageSizeOptions]="[25, 50, 75, 100, 125]"></mat-paginator>

View File

@ -15,12 +15,7 @@
<!-- Menu --> <!-- Menu -->
<div class="menu-slot"> <div class="menu-slot">
<button <button type="button" mat-icon-button *ngIf="isAllowed('changePersonal')" [matMenuTriggerFor]="userExtraMenu">
type="button"
mat-icon-button
*ngIf="isAllowed('changePersonal')"
[matMenuTriggerFor]="userExtraMenu"
>
<mat-icon>more_vert</mat-icon> <mat-icon>more_vert</mat-icon>
</button> </button>
</div> </div>
@ -49,7 +44,6 @@
<form <form
[ngClass]="{ 'mat-form-field-enabled': editUser }" [ngClass]="{ 'mat-form-field-enabled': editUser }"
[formGroup]="personalInfoForm" [formGroup]="personalInfoForm"
(ngSubmit)="saveUser()"
*ngIf="user" *ngIf="user"
(keydown)="onKeyDown($event)" (keydown)="onKeyDown($event)"
> >
@ -104,7 +98,7 @@
placeholder="{{ 'Email' | translate }}" placeholder="{{ 'Email' | translate }}"
name="email" name="email"
formControlName="email" formControlName="email"
[value]="user.email ? user.email: null" [value]="user.email ? user.email : null"
/> />
<mat-error *ngIf="personalInfoForm.get('email').hasError('email')" translate> <mat-error *ngIf="personalInfoForm.get('email').hasError('email')" translate>
Please enter a valid email address Please enter a valid email address