Recover Password UI

This commit is contained in:
SheenaMalhotra182
2022-09-07 13:00:02 +05:30
parent 2fd10b2fe4
commit cd0a30ee04
10 changed files with 592 additions and 232 deletions

View File

@@ -306,11 +306,30 @@
"HELP": "NEED HELP?", "HELP": "NEED HELP?",
"REGISTER": "REGISTER" "REGISTER": "REGISTER"
}, },
"FORGOT-PASSWORD": {
"FORGOT-PASSWORD": "Forgot Password?"
},
"DIALOG": { "DIALOG": {
"CANCEL": "Cancel", "CANCEL": "Cancel",
"CHOOSE": "Choose" "CHOOSE": "Choose"
} }
}, },
"RECOVER-PASSWORD": {
"RECOVER-PASSWORD": "Recover Password",
"MESSAGES": {
"EMAIL-ID-REQUIRED": "Required",
"FILLER": "Don't worry, happens to the best of us.",
"ENTER-EMAIL": "Enter Email Addresss on your account",
"INVALID-EMAIL": "You've entered an invalid Email ID",
"RESET-PASSWORD-EMAIL-SENT": "An email has been sent to your registered Email ID. Please click this link when get it."
},
"PLACEHOLDERS": {
"EMAIL-ADDRESS": "Email Address"
},
"BUTTONS": {
"SEND-INSTRUCTIONS": "Send Instructions"
}
},
"ADF-DATATABLE": { "ADF-DATATABLE": {
"EMPTY": { "EMPTY": {
"HEADER": "This list is empty", "HEADER": "This list is empty",

View File

@@ -117,6 +117,13 @@
(change)="rememberMe = !rememberMe">{{ 'LOGIN.LABEL.REMEMBER' | translate }} (change)="rememberMe = !rememberMe">{{ 'LOGIN.LABEL.REMEMBER' | translate }}
</mat-checkbox> </mat-checkbox>
</div> </div>
<div class="adf-login-action" *ngIf="forgotPasswordStatus">
<div id="adf-login-action" class="adf-login-action adf-full-width">
<!-- <a [routerLink]=" '/recover-password' ">Forgot Password?</a> -->
<a style="cursor: pointer;" (click)="forgotPassword()">{{ 'LOGIN.FORGOT-PASSWORD.FORGOT-PASSWORD' | translate }}</a>
</div>
</div>
</div> </div>
<div *ngIf="implicitFlow"> <div *ngIf="implicitFlow">

View File

@@ -38,6 +38,8 @@ import { OauthConfigModel } from '../../models/oauth-config.model';
import { DomSanitizer, SafeStyle } from '@angular/platform-browser'; import { DomSanitizer, SafeStyle } from '@angular/platform-browser';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { RecoverPasswordDialogService } from './recover-password-dialog.service';
import { RecoverPasswordComponent } from './recover-password/recover-password.component';
// eslint-disable-next-line no-shadow // eslint-disable-next-line no-shadow
enum LoginSteps { enum LoginSteps {
@@ -52,11 +54,11 @@ interface ValidationMessage {
} }
@Component({ @Component({
selector: 'adf-login', selector: "adf-login",
templateUrl: './login.component.html', templateUrl: "./login.component.html",
styleUrls: ['./login.component.scss'], styleUrls: ["./login.component.scss"],
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
host: { class: 'adf-login' } host: { class: "adf-login" },
}) })
export class LoginComponent implements OnInit, OnDestroy { export class LoginComponent implements OnInit, OnDestroy {
isPasswordShow: boolean = false; isPasswordShow: boolean = false;
@@ -75,23 +77,24 @@ export class LoginComponent implements OnInit, OnDestroy {
/** Sets the URL of the NEED HELP link in the footer. */ /** Sets the URL of the NEED HELP link in the footer. */
@Input() @Input()
needHelpLink: string = ''; needHelpLink: string = "";
/** Sets the URL of the REGISTER link in the footer. */ /** Sets the URL of the REGISTER link in the footer. */
@Input() @Input()
registerLink: string = ''; registerLink: string = "";
/** Path to a custom logo image. */ /** Path to a custom logo image. */
@Input() @Input()
logoImageUrl: string = './assets/images/alfresco-logo.svg'; logoImageUrl: string = "./assets/images/alfresco-logo.svg";
/** Path to a custom background image. */ /** Path to a custom background image. */
@Input() @Input()
backgroundImageUrl: string = './assets/images/background.svg'; backgroundImageUrl: string = "./assets/images/background.svg";
/** The copyright text below the login box. */ /** The copyright text below the login box. */
@Input() @Input()
copyrightText: string = '\u00A9 2016 Alfresco Software, Inc. All Rights Reserved.'; copyrightText: string =
"\u00A9 2016 Alfresco Software, Inc. All Rights Reserved.";
/** Custom validation rules for the login form. */ /** Custom validation rules for the login form. */
@Input() @Input()
@@ -121,6 +124,7 @@ export class LoginComponent implements OnInit, OnDestroy {
actualLoginStep: any = LoginSteps.Landing; actualLoginStep: any = LoginSteps.Landing;
LoginSteps = LoginSteps; LoginSteps = LoginSteps;
rememberMe: boolean = true; rememberMe: boolean = true;
forgotPasswordStatus: boolean = true;
formError: { [id: string]: string }; formError: { [id: string]: string };
minLength: number = 2; minLength: number = 2;
footerTemplate: TemplateRef<any>; footerTemplate: TemplateRef<any>;
@@ -140,23 +144,29 @@ export class LoginComponent implements OnInit, OnDestroy {
private userPreferences: UserPreferencesService, private userPreferences: UserPreferencesService,
private route: ActivatedRoute, private route: ActivatedRoute,
private sanitizer: DomSanitizer, private sanitizer: DomSanitizer,
private alfrescoApiService: AlfrescoApiService private alfrescoApiService: AlfrescoApiService,
) { private dialogService: RecoverPasswordDialogService
} ) {}
ngOnInit() { ngOnInit() {
this.initFormError(); this.initFormError();
this.initFormFieldsDefault(); this.initFormFieldsDefault();
this.initFormFieldsMessages(); this.initFormFieldsMessages();
this.successRoute = this.appConfig.get<string>('successRoute', this.successRoute); this.successRoute = this.appConfig.get<string>(
"successRoute",
this.successRoute
);
if (this.authService.isLoggedIn()) { if (this.authService.isLoggedIn()) {
this.router.navigate([this.successRoute]); this.router.navigate([this.successRoute]);
} else { } else {
if (this.authService.isOauth()) { if (this.authService.isOauth()) {
const oauth: OauthConfigModel = this.appConfig.get<OauthConfigModel>(AppConfigValues.OAUTHCONFIG, null); const oauth: OauthConfigModel =
this.appConfig.get<OauthConfigModel>(
AppConfigValues.OAUTHCONFIG,
null
);
if (oauth && oauth.silentLogin) { if (oauth && oauth.silentLogin) {
this.redirectToImplicitLogin(); this.redirectToImplicitLogin();
} else if (oauth && oauth.implicitFlow) { } else if (oauth && oauth.implicitFlow) {
@@ -165,8 +175,10 @@ export class LoginComponent implements OnInit, OnDestroy {
} }
this.route.queryParams.subscribe((params: Params) => { this.route.queryParams.subscribe((params: Params) => {
const url = params['redirectUrl']; const url = params["redirectUrl"];
const provider = this.appConfig.get<string>(AppConfigValues.PROVIDERS); const provider = this.appConfig.get<string>(
AppConfigValues.PROVIDERS
);
this.authService.setRedirect({ provider, url }); this.authService.setRedirect({ provider, url });
}); });
@@ -178,7 +190,7 @@ export class LoginComponent implements OnInit, OnDestroy {
this.form.valueChanges this.form.valueChanges
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntil(this.onDestroy$))
.subscribe(data => this.onValueChanged(data)); .subscribe((data) => this.onValueChanged(data));
} }
ngOnDestroy() { ngOnDestroy() {
@@ -190,6 +202,21 @@ export class LoginComponent implements OnInit, OnDestroy {
this.onSubmit(this.form.value); this.onSubmit(this.form.value);
} }
forgotPassword() {
this.dialogService
.showDialog(RecoverPasswordComponent, {
data: {
title: "Recover",
},
})
.afterClosed();
// .subscribe((result) => {
// if (result) {
// }
// });
}
redirectToImplicitLogin() { redirectToImplicitLogin() {
this.alfrescoApiService.getInstance().oauth2Auth.implicitLogin(); this.alfrescoApiService.getInstance().oauth2Auth.implicitLogin();
} }
@@ -204,7 +231,7 @@ export class LoginComponent implements OnInit, OnDestroy {
this.disableError(); this.disableError();
const args = new LoginSubmitEvent({ const args = new LoginSubmitEvent({
controls: { username: this.form.controls.username } controls: { username: this.form.controls.username },
}); });
this.executeSubmit.emit(args); this.executeSubmit.emit(args);
@@ -229,9 +256,9 @@ export class LoginComponent implements OnInit, OnDestroy {
this.disableError(); this.disableError();
for (const field in this.formError) { for (const field in this.formError) {
if (field) { if (field) {
this.formError[field] = ''; this.formError[field] = "";
const hasError = const hasError =
(this.form.controls[field].errors && data[field] !== '') || (this.form.controls[field].errors && data[field] !== "") ||
(this.form.controls[field].dirty && (this.form.controls[field].dirty &&
!this.form.controls[field].valid); !this.form.controls[field].valid);
if (hasError) { if (hasError) {
@@ -239,7 +266,11 @@ export class LoginComponent implements OnInit, OnDestroy {
if (key) { if (key) {
const message = this._message[field][key]; const message = this._message[field][key];
if (message && message.value) { if (message && message.value) {
const translated = this.translateService.instant(message.value, message.params); const translated =
this.translateService.instant(
message.value,
message.params
);
this.formError[field] += translated; this.formError[field] += translated;
} }
} }
@@ -277,7 +308,7 @@ export class LoginComponent implements OnInit, OnDestroy {
this.isError = true; this.isError = true;
this.error.emit(new LoginErrorEvent(err)); this.error.emit(new LoginErrorEvent(err));
}, },
() => this.logService.info('Login done') () => this.logService.info("Login done")
); );
} }
@@ -288,22 +319,22 @@ export class LoginComponent implements OnInit, OnDestroy {
if ( if (
err.error && err.error &&
err.error.crossDomain && err.error.crossDomain &&
err.error.message.indexOf('Access-Control-Allow-Origin') !== -1 err.error.message.indexOf("Access-Control-Allow-Origin") !== -1
) { ) {
this.errorMsg = err.error.message; this.errorMsg = err.error.message;
} else if ( } else if (
err.status === 403 && err.status === 403 &&
err.message.indexOf('Invalid CSRF-token') !== -1 err.message.indexOf("Invalid CSRF-token") !== -1
) { ) {
this.errorMsg = 'LOGIN.MESSAGES.LOGIN-ERROR-CSRF'; this.errorMsg = "LOGIN.MESSAGES.LOGIN-ERROR-CSRF";
} else if ( } else if (
err.status === 403 && err.status === 403 &&
err.message.indexOf('The system is currently in read-only mode') !== err.message.indexOf("The system is currently in read-only mode") !==
-1 -1
) { ) {
this.errorMsg = 'LOGIN.MESSAGES.LOGIN-ECM-LICENSE'; this.errorMsg = "LOGIN.MESSAGES.LOGIN-ECM-LICENSE";
} else { } else {
this.errorMsg = 'LOGIN.MESSAGES.LOGIN-ERROR-CREDENTIALS'; this.errorMsg = "LOGIN.MESSAGES.LOGIN-ERROR-CREDENTIALS";
} }
} }
@@ -332,7 +363,7 @@ export class LoginComponent implements OnInit, OnDestroy {
) { ) {
this._message[field][ruleId] = { this._message[field][ruleId] = {
value: msg, value: msg,
params params,
}; };
} }
@@ -361,7 +392,9 @@ export class LoginComponent implements OnInit, OnDestroy {
} }
getBackgroundUrlImageUrl(): SafeStyle { getBackgroundUrlImageUrl(): SafeStyle {
return this.sanitizer.bypassSecurityTrustStyle(`url(${this.backgroundImageUrl})`); return this.sanitizer.bypassSecurityTrustStyle(
`url(${this.backgroundImageUrl})`
);
} }
/** /**
@@ -369,8 +402,8 @@ export class LoginComponent implements OnInit, OnDestroy {
*/ */
private initFormError() { private initFormError() {
this.formError = { this.formError = {
username: '', username: "",
password: '' password: "",
}; };
} }
@@ -381,28 +414,33 @@ export class LoginComponent implements OnInit, OnDestroy {
this._message = { this._message = {
username: { username: {
required: { required: {
value: 'LOGIN.MESSAGES.USERNAME-REQUIRED' value: "LOGIN.MESSAGES.USERNAME-REQUIRED",
}, },
minlength: { minlength: {
value: 'LOGIN.MESSAGES.USERNAME-MIN', value: "LOGIN.MESSAGES.USERNAME-MIN",
params: { params: {
minLength: this.minLength minLength: this.minLength,
} },
} },
}, },
password: { password: {
required: { required: {
value: 'LOGIN.MESSAGES.PASSWORD-REQUIRED' value: "LOGIN.MESSAGES.PASSWORD-REQUIRED",
} },
} },
}; };
} }
private initFormFieldsDefault() { private initFormFieldsDefault() {
this.form = this._fb.group({ this.form = this._fb.group({
username: ['', Validators.compose([Validators.required, Validators.minLength(this.minLength)])], username: [
password: ['', Validators.required] "",
Validators.compose([
Validators.required,
Validators.minLength(this.minLength),
]),
],
password: ["", Validators.required],
}); });
} }

View File

@@ -0,0 +1,24 @@
/*
* Copyright © 2005 - 2021 Alfresco Software, Ltd. All rights reserved.
*
* License rights for this program may be obtained from Alfresco Software, Ltd.
* pursuant to a written agreement and any use of this program without such an
* agreement is prohibited.
*/
import { Injectable, TemplateRef } from '@angular/core';
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { ComponentType } from '@angular/cdk/portal';
@Injectable({ providedIn: 'root' })
export class RecoverPasswordDialogService {
constructor(private dialog: MatDialog) { }
showDialog<T>(dialog: ComponentType<T> | TemplateRef<T>, options = {}): MatDialogRef<T, boolean> {
return this.dialog.open<T>(dialog, {
height: '35%',
minWidth: '30%',
...options
});
}
}

View File

@@ -0,0 +1,102 @@
<div class="aaa-dialog " >
<div class="resettingPassword" >
<div class="aaa-dialog-header">
<div class="aaa-mat-dialog-title" mat-dialog-title style="font-weight:bold ; font-size: large ;">{{ 'RECOVER-PASSWORD.RECOVER-PASSWORD' | translate }} </div>
<hr>
</div>
<div #DialogBodyContainer class="aaa-dialog-body" *ngIf="passwordResetStatus != true ">
<p style="font-size: medium;">{{ 'RECOVER-PASSWORD.MESSAGES.FILLER' | translate }}</p>
<!-- <mat-card class="apa-form-field-editor-card">
<mat-card-title>Recover</mat-card-title>
<ng-container >
<mat-card-content >
</mat-card-content>
</ng-container>
</mat-card> -->
<form [formGroup]="recoverPasswordForm" >
<label style="font-weight: bold;font-size:medium;">{{ 'RECOVER-PASSWORD.MESSAGES.ENTER-EMAIL' | translate }}</label><br>
<!-- <input data-automation-id="security-control-name-input-text" placeholder="Email Address" formControlName="emailId" required> -->
<mat-form-field appearance="fill" style="padding-top: 5px; width:100% ;" >
<input
data-automation-id="security-control-name-input-text" placeholder="{{ 'RECOVER-PASSWORD.PLACEHOLDERS.EMAIL-ADDRESS' | translate }}"
matInput width="100%"
formControlName="emailId"
required>
<mat-error *ngIf="recoverPasswordForm.controls.emailId?.hasError('required')">{{ 'RECOVER-PASSWORD.MESSAGES.EMAIL-ID-REQUIRED' | translate }}</mat-error>
<mat-error
*ngIf="recoverPasswordForm.controls.emailId.errors?.pattern && recoverPasswordForm.controls.emailId?.touched ">
{{ 'RECOVER-PASSWORD.MESSAGES.INVALID-EMAIL' | translate }}</mat-error>
</mat-form-field> <br>
<br>
<!-- <button width="100%" class="RECOVER-PASSWORD-btn aaa-full-width" style="color:white;"
mat-button
id="saveButton"
color="primary"
[disabled]="isSaveDisabled() || recoverPasswordForm.controls.emailId.value == '' || recoverPasswordForm.controls.emailId.errors?.pattern"
(click)="sendInstructions()">
{{ RECOVER-PASSWORD.BUTTONS.SEND-INSTRUCTIONS' | translate }}
</button><br> -->
<div>
<button type="button" (click)="sendInstructions()" id="send-instructions-btn"
[attr.aria-label]="'RECOVER-PASSWORD.BUTTONS.SEND-INSTRUCTIONS' | translate"
[disabled]="isSaveDisabled() || recoverPasswordForm.controls.emailId.value == '' || recoverPasswordForm.controls.emailId.errors?.pattern"
class="adf-login-button"
mat-raised-button color="primary"
data-automation-id="send-instructions-button">
<span class="adf-login-button-label"> {{ 'RECOVER-PASSWORD.BUTTONS.SEND-INSTRUCTIONS' | translate }} </span>
</button>
</div>
<br>
<!-- <adf-card-view-textitem></adf-card-view-textitem> -->
</form>
</div>
<!-- <div class="aaa-dialog-footer">
<button class="btn btn-info">NEEEEWWWW</button>
<button mat-button color="primary">Primary</button>
</div> -->
<div #DialogBodyContainer class="aaa-dialog-body" *ngIf="passwordResetStatus == true ">
<div class="passwordResetSuccessful" *ngIf="passwordResetStatus == true ">
<p style="font-size: medium;">
<a style="color: blue; text-decoration:underline; cursor: pointer;">{{ 'RECOVER-PASSWORD.MESSAGES.RESET-PASSWORD-EMAIL-SENT' | translate }}</a>
</p>
</div>
<br>
</div>
</div>
</div>
<!-- <adf-card-view
[properties]="properties"
[editable]="false">
</adf-card-view> -->

View File

@@ -0,0 +1,54 @@
.aaa {
&-dialog {
display: flex;
flex-direction: column;
height: 50%;
&-body {
flex: 1;
overflow-y: auto;
padding: 0 15px;
}
&-footer {
display: flex;
justify-content: flex-end;
align-self: flex-end;
}
}
&-security-controls {
&-actions {
padding: 15px 10px;
}
&-toggle-description-text {
color: var(--theme-accent-color);
font-weight: bold;
cursor: pointer;
}
&-config-type-image-container {
display: flex;
width: 100%;
flex-direction: row;
justify-content: center;
}
&-config-type-image {
width: 100%;
}
}
}
.aaa-dialog{
width: 100%;
height: 100%;
}
.recover-password-btn{
background: var(--theme-blue-text-background);
border: 1px solid var(--theme-blue-border-color);
font-size: var(--theme-button-font-size);
}
// mat-dialog-container{
// border: 1px solid black;
// border-radius: 50px;
// }

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RecoverPasswordComponent } from './recover-password.component';
describe('RecoverPasswordComponent', () => {
let component: RecoverPasswordComponent;
let fixture: ComponentFixture<RecoverPasswordComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ RecoverPasswordComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(RecoverPasswordComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,91 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2020 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
//import { AlfrescoApiService, AppConfigService, AuthenticationService, CardViewArrayItemModel, CardViewBoolItemModel, CardViewDateItemModel, CardViewDatetimeItemModel, CardViewFloatItemModel, CardViewIntItemModel, CardViewKeyValuePairsItemModel, CardViewMapItemModel, CardViewSelectItemModel, CardViewTextItemModel, LogService, TranslationService, UserPreferencesService } from '@alfresco/adf-core';
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { TranslationService } from '../../../../..';
//import { MatDialogRef } from '@angular/material/dialog';
// import { FormBuilder } from '@angular/forms';
// import { DomSanitizer } from '@angular/platform-browser';
// import { ActivatedRoute, Router } from '@angular/router';
// import { of } from 'rxjs';
@Component({
selector: 'aaa-recover-password',
templateUrl: './recover-password.component.html',
styleUrls: ['./recover-password.component.scss']
})
export class RecoverPasswordComponent implements OnInit{
recoverPasswordForm: FormGroup;
saveInProgress : boolean = false;
passwordResetStatus : boolean = false;
properties = [{label: 'My Label', value: 'My value'}];
@ViewChild('DialogBodyContainer') dialogBodyContainer: ElementRef;
constructor(private formBuilder: FormBuilder,
private translateService: TranslationService,
//private matDialogRef: MatDialogRef<RecoverPasswordComponent>,
//@Inject(MAT_DIALOG_DATA) private data: any
) {
this.translateService.loadTranslation('en');
this.translateService.use('en');
}
ngOnInit(): void {
this.recoverPasswordForm = this.formBuilder.group({
emailId:['',[Validators.required, Validators.pattern('^[a-zA-Z0-9+_.-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9.-]{2,}$')]]
})
}
sendInstructions() {
this.saveInProgress = true;
console.log("Sending Email to ", this.recoverPasswordForm.controls.emailId.value);
this.passwordResetStatus = true;
//this.matDialogRef.close();
}
isSaveDisabled(): boolean {
return !this.recoverPasswordForm.valid || this.saveInProgress;
}
}

View File

@@ -27,6 +27,7 @@ import { LoginFooterDirective } from './directives/login-footer.directive';
import { LoginHeaderDirective } from './directives/login-header.directive'; import { LoginHeaderDirective } from './directives/login-header.directive';
import { LoginDialogComponent } from './components/login-dialog.component'; import { LoginDialogComponent } from './components/login-dialog.component';
import { LoginDialogPanelComponent } from './components/login-dialog-panel.component'; import { LoginDialogPanelComponent } from './components/login-dialog-panel.component';
import { RecoverPasswordComponent } from './components/recover-password/recover-password.component';
@NgModule({ @NgModule({
imports: [ imports: [
@@ -42,7 +43,8 @@ import { LoginDialogPanelComponent } from './components/login-dialog-panel.compo
LoginFooterDirective, LoginFooterDirective,
LoginHeaderDirective, LoginHeaderDirective,
LoginDialogComponent, LoginDialogComponent,
LoginDialogPanelComponent LoginDialogPanelComponent,
RecoverPasswordComponent
], ],
exports: [ exports: [
LoginComponent, LoginComponent,

372
package-lock.json generated

File diff suppressed because it is too large Load Diff