Commit da3ebdcd authored by Steven Zou's avatar Steven Zou
Browse files

merge ui_ng

parent c1d9d682
# http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = 0
trim_trailing_whitespace = true
# Indentation override
#[lib/**.js]
#[{package.json,.travis.yml}]
#[**/**.js]
coverage/
dist/
html-report/
node_modules/
typings/
**/*npm-debug.log.*
**/*yarn-error.log.*
.idea/
.DS_Store
language: node_js
node_js:
- "6.9"
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
......@@ -38,14 +38,7 @@
</div>
</section>
</form>
<div style="height: 30px;"></div>
<clr-alert [clrAlertType]="'alert-danger'" [clrAlertClosable]="true" [(clrAlertClosed)]="alertClose">
<div class="alert-item">
<span class="alert-text">
{{errorMessage}}
</span>
</div>
</clr-alert>
<inline-alert (confirmEvt)="confirmCancel($event)"></inline-alert>
</div>
<div class="modal-footer">
<span class="spinner spinner-inline" style="top:8px;" [hidden]="showProgress === false"></span>
......
......@@ -3,6 +3,10 @@ import { NgForm } from '@angular/forms';
import { SessionUser } from '../../shared/session-user';
import { SessionService } from '../../shared/session.service';
import { MessageService } from '../../global-message/message.service';
import { AlertType, httpStatusCode } from '../../shared/shared.const';
import { errorHandler, accessErrorHandler } from '../../shared/shared.utils';
import { InlineAlertComponent } from '../../shared/inline-alert/inline-alert.component';
@Component({
selector: "account-settings-modal",
......@@ -13,43 +17,52 @@ export class AccountSettingsModalComponent implements OnInit, AfterViewChecked {
opened: boolean = false;
staticBackdrop: boolean = true;
account: SessionUser;
error: any;
alertClose: boolean = true;
error: any = null;
originalStaticData: SessionUser;
private isOnCalling: boolean = false;
private formValueChanged: boolean = false;
accountFormRef: NgForm;
@ViewChild("accountSettingsFrom") accountForm: NgForm;
@ViewChild(InlineAlertComponent)
private inlineAlert: InlineAlertComponent;
constructor(private session: SessionService) { }
constructor(
private session: SessionService,
private msgService: MessageService) { }
ngOnInit(): void {
//Value copy
this.account = Object.assign({}, this.session.getCurrentUser());
}
private isUserDataChange(): boolean {
if (!this.originalStaticData || !this.account) {
return false;
}
for (var prop in this.originalStaticData) {
if (this.originalStaticData[prop]) {
if (this.account[prop]) {
if (this.originalStaticData[prop] != this.account[prop]) {
return true;
}
}
}
}
return false;
}
public get isValid(): boolean {
return this.accountForm && this.accountForm.valid;
return this.accountForm && this.accountForm.valid && this.error === null;
}
public get showProgress(): boolean {
return this.isOnCalling;
}
public get errorMessage(): string {
if(this.error){
if(this.error.message){
return this.error.message;
}else{
if(this.error._body){
return this.error._body;
}
}
}
return "";
}
ngAfterViewChecked(): void {
if (this.accountFormRef != this.accountForm) {
this.accountFormRef = this.accountForm;
......@@ -59,12 +72,15 @@ export class AccountSettingsModalComponent implements OnInit, AfterViewChecked {
this.error = null;
}
this.formValueChanged = true;
this.inlineAlert.close();
});
}
}
}
open() {
//Keep the initial data for future diff
this.originalStaticData = Object.assign({}, this.session.getCurrentUser());
this.account = Object.assign({}, this.session.getCurrentUser());
this.formValueChanged = false;
......@@ -72,7 +88,18 @@ export class AccountSettingsModalComponent implements OnInit, AfterViewChecked {
}
close() {
this.opened = false;
if (this.formValueChanged) {
if (!this.isUserDataChange()) {
this.opened = false;
} else {
//Need user confirmation
this.inlineAlert.showInlineConfirmation({
message: "ALERT.FORM_CHANGE_CONFIRMATION"
});
}
} else {
this.opened = false;
}
}
submit() {
......@@ -92,12 +119,22 @@ export class AccountSettingsModalComponent implements OnInit, AfterViewChecked {
.then(() => {
this.isOnCalling = false;
this.close();
this.msgService.announceMessage(200, "PROFILE.SAVE_SUCCESS", AlertType.SUCCESS);
})
.catch(error => {
this.isOnCalling = false;
this.error = error;
this.alertClose = false;
if(accessErrorHandler(error, this.msgService)){
this.opened = false;
}else{
this.inlineAlert.showInlineError(error);
}
});
}
confirmCancel(): void {
this.inlineAlert.close();
this.opened = false;
}
}
\ No newline at end of file
......@@ -6,6 +6,9 @@ import { SignInComponent } from './sign-in/sign-in.component';
import { PasswordSettingComponent } from './password/password-setting.component';
import { AccountSettingsModalComponent } from './account-settings/account-settings-modal.component';
import { SharedModule } from '../shared/shared.module';
import { SignUpComponent } from './sign-up/sign-up.component';
import { ForgotPasswordComponent } from './password/forgot-password.component';
import { ResetPasswordComponent } from './password/reset-password.component';
import { PasswordSettingService } from './password/password-setting.service';
......@@ -15,9 +18,19 @@ import { PasswordSettingService } from './password/password-setting.service';
RouterModule,
SharedModule
],
declarations: [SignInComponent, PasswordSettingComponent, AccountSettingsModalComponent],
exports: [SignInComponent, PasswordSettingComponent, AccountSettingsModalComponent],
declarations: [
SignInComponent,
PasswordSettingComponent,
AccountSettingsModalComponent,
SignUpComponent,
ForgotPasswordComponent,
ResetPasswordComponent],
exports: [
SignInComponent,
PasswordSettingComponent,
AccountSettingsModalComponent,
ResetPasswordComponent],
providers: [PasswordSettingService]
})
export class AccountModule { }
\ No newline at end of file
<clr-modal [(clrModalOpen)]="opened" [clrModalStaticBackdrop]="true">
<h3 class="modal-title">{{'RESET_PWD.TITLE' | translate}}</h3>
<label class="modal-title reset-modal-title-override">{{'RESET_PWD.CAPTION' | translate}}</label>
<div class="modal-body" style="overflow-y: hidden;">
<form #forgotPasswordFrom="ngForm" class="form">
<section class="form-block">
<div class="form-group">
<label for="reset_pwd_email" class="col-md-4 required">{{'RESET_PWD.EMAIL' | translate}}</label>
<label for="reset_pwd_email" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]="validationState === false">
<input name="reset_pwd_email" type="text" #eamilInput="ngModel" [(ngModel)]="email"
required
pattern='^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$'
id="reset_pwd_email"
size="36"
(input)="handleValidation(true)"
(focusout)="handleValidation(false)">
<span class="tooltip-content">
{{'TOOLTIP.EMAIL' | translate}}
</span>
</label>
</div>
</section>
</form>
<inline-alert></inline-alert>
<div style="height: 30px;"></div>
</div>
<div class="modal-footer">
<span class="spinner spinner-inline" style="top:8px;" [hidden]="showProgress === false"></span>
<button type="button" class="btn btn-outline" (click)="close()">{{'BUTTON.CANCEL' | translate}}</button>
<button type="button" class="btn btn-primary" [disabled]="!isValid || showProgress" (click)="send()">{{'BUTTON.SEND' | translate}}</button>
</div>
</clr-modal>
\ No newline at end of file
import { Component } from '@angular/core';
import { Component, ViewChild } from '@angular/core';
import { Router } from '@angular/router';
import { NgForm } from '@angular/forms';
import { PasswordSettingService } from './password-setting.service';
import { InlineAlertComponent } from '../../shared/inline-alert/inline-alert.component';
@Component({
selector: 'forgot-password',
templateUrl: "forgot-password.component.html"
templateUrl: "forgot-password.component.html",
styleUrls: ['password.component.css']
})
export class ForgotPasswordComponent {
// constructor(private router: Router){}
opened: boolean = false;
private onGoing: boolean = false;
private email: string = "";
private validationState: boolean = true;
private forceValid: boolean = true;
@ViewChild("forgotPasswordFrom") forgotPwdForm: NgForm;
@ViewChild(InlineAlertComponent)
private inlineAlert: InlineAlertComponent;
constructor(private pwdService: PasswordSettingService) { }
public get showProgress(): boolean {
return this.onGoing;
}
public get isValid(): boolean {
return this.forgotPwdForm && this.forgotPwdForm.valid && this.forceValid;
}
public open(): void {
this.opened = true;
this.validationState = true;
this.forceValid = true;
this.forgotPwdForm.resetForm();
}
public close(): void {
this.opened = false;
}
public send(): void {
//Double confirm to avoid improper situations
if (!this.email) {
return;
}
if (!this.isValid) {
return;
}
this.onGoing = true;
this.pwdService.sendResetPasswordMail(this.email)
.then(response => {
this.onGoing = false;
this.forceValid = false;//diable the send button
this.inlineAlert.showInlineSuccess({
message: "RESET_PWD.SUCCESS"
});
})
.catch(error => {
this.onGoing = false;
this.inlineAlert.showInlineError(error);
})
}
public handleValidation(flag: boolean): void {
if (flag) {
this.validationState = true;
} else {
this.validationState = this.isValid;
}
}
}
\ No newline at end of file
......@@ -45,6 +45,7 @@
</label>
</div>
</section>
<inline-alert (confirmEvt)="confirmCancel($event)"></inline-alert>
</form>
</div>
<div class="modal-footer">
......
......@@ -4,6 +4,10 @@ import { NgForm } from '@angular/forms';
import { PasswordSettingService } from './password-setting.service';
import { SessionService } from '../../shared/session.service';
import { AlertType, httpStatusCode } from '../../shared/shared.const';
import { MessageService } from '../../global-message/message.service';
import { errorHandler, isEmptyForm, accessErrorHandler } from '../../shared/shared.utils';
import { InlineAlertComponent } from '../../shared/inline-alert/inline-alert.component';
@Component({
selector: 'password-setting',
......@@ -14,19 +18,27 @@ export class PasswordSettingComponent implements AfterViewChecked {
oldPwd: string = "";
newPwd: string = "";
reNewPwd: string = "";
error: any = null;
private formValueChanged: boolean = false;
private onCalling: boolean = false;
pwdFormRef: NgForm;
@ViewChild("changepwdForm") pwdForm: NgForm;
constructor(private passwordService: PasswordSettingService, private session: SessionService){}
@ViewChild(InlineAlertComponent)
private inlineAlert: InlineAlertComponent;
constructor(
private passwordService: PasswordSettingService,
private session: SessionService,
private msgService: MessageService) { }
//If form is valid
public get isValid(): boolean {
if (this.pwdForm && this.pwdForm.form.get("newPassword")) {
return this.pwdForm.valid &&
this.pwdForm.form.get("newPassword").value === this.pwdForm.form.get("reNewPassword").value;
(this.pwdForm.form.get("newPassword").value === this.pwdForm.form.get("reNewPassword").value) &&
this.error === null;
}
return false;
}
......@@ -45,6 +57,8 @@ export class PasswordSettingComponent implements AfterViewChecked {
if (this.pwdFormRef) {
this.pwdFormRef.valueChanges.subscribe(data => {
this.formValueChanged = true;
this.error = null;
this.inlineAlert.close();
});
}
}
......@@ -54,10 +68,26 @@ export class PasswordSettingComponent implements AfterViewChecked {
open(): void {
this.opened = true;
this.pwdForm.reset();
this.formValueChanged = false;
}
//Close the moal dialog
close(): void {
if (this.formValueChanged) {
if (isEmptyForm(this.pwdForm)) {
this.opened = false;
} else {
//Need user confirmation
this.inlineAlert.showInlineConfirmation({
message: "ALERT.FORM_CHANGE_CONFIRMATION"
});
}
} else {
this.opened = false;
}
}
confirmCancel(): void {
this.opened = false;
}
......@@ -73,26 +103,31 @@ export class PasswordSettingComponent implements AfterViewChecked {
//Double confirm session is valid
let cUser = this.session.getCurrentUser();
if(!cUser){
if (!cUser) {
return;
}
//Call service
this.onCalling = true;
this.passwordService.changePassword(cUser.user_id,
{
new_password: this.pwdForm.value.newPassword,
old_password: this.pwdForm.value.oldPassword
})
.then(() => {
this.onCalling = false;
this.close();
})
.catch(error => {
this.onCalling = false;
console.error(error);//TODO:
});
//TODO:publish the successful message to general messae box
this.passwordService.changePassword(cUser.user_id,
{
new_password: this.pwdForm.value.newPassword,
old_password: this.pwdForm.value.oldPassword
})
.then(() => {
this.onCalling = false;
this.close();
this.msgService.announceMessage(200, "CHANGE_PWD.SAVE_SUCCESS", AlertType.SUCCESS);
})
.catch(error => {
this.onCalling = false;
this.error = error;
if(accessErrorHandler(error, this.msgService)){
this.opened = false;
}else{
this.inlineAlert.showInlineError(error);
}
});
}
}
\ No newline at end of file
......@@ -5,6 +5,8 @@ import 'rxjs/add/operator/toPromise';
import { PasswordSetting } from './password-setting';
const passwordChangeEndpoint = "/api/users/:user_id/password";
const sendEmailEndpoint = "/sendEmail";
const resetPasswordEndpoint = "/reset";
@Injectable()
export class PasswordSettingService {
......@@ -19,17 +21,46 @@ export class PasswordSettingService {
constructor(private http: Http) { }
changePassword(userId: number, setting: PasswordSetting): Promise<any> {
if(!setting || setting.new_password.trim()==="" || setting.old_password.trim()===""){
if (!setting || setting.new_password.trim() === "" || setting.old_password.trim() === "") {
return Promise.reject("Invalid data");
}
let putUrl = passwordChangeEndpoint.replace(":user_id", userId+"");
let putUrl = passwordChangeEndpoint.replace(":user_id", userId + "");
return this.http.put(putUrl, JSON.stringify(setting), this.options)
.toPromise()
.then(() => null)
.catch(error=>{
return Promise.reject(error);
});
.toPromise()
.then(() => null)
.catch(error => {
return Promise.reject(error);
});
}
sendResetPasswordMail(email: string): Promise<any> {
if (!email) {
return Promise.reject("Invalid email");
}
let getUrl = sendEmailEndpoint + "?email=" + email;
return this.http.get(getUrl, this.options).toPromise()
.then(response => response)
.catch(error => {
return Promise.reject(error);
})
}
resetPassword(uuid: string, newPassword: string): Promise<any> {
if (!uuid || !newPassword) {
return Promise.reject("Invalid reset uuid or password");
}
return this.http.post(resetPasswordEndpoint, JSON.stringify({
"reset_uuid": uuid,
"password": newPassword
}), this.options)
.toPromise()
.then(response => response)
.catch(error => {
return Promise.reject(error);
});
}
}
.reset-modal-title-override {
font-size: 14px !important;
}
\ No newline at end of file
<clr-modal [(clrModalOpen)]="opened" [clrModalStaticBackdrop]="true">
<h3 class="modal-title">{{'RESET_PWD.TITLE' | translate}}</h3>
<label class="modal-title reset-modal-title-override">{{'RESET_PWD.CAPTION2' | translate}}</label>
<div class="modal-body" style="overflow-y: hidden;">
<form #resetPwdForm="ngForm" class="form">
<section class="form-block">
<div class="form-group">
<label for="newPassword">{{'CHANGE_PWD.NEW_PWD' | translate}}</label>
<label for="newPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]='getValidationState("newPassword") === false'>
<input type="password" id="newPassword" placeholder='{{"PLACEHOLDER.NEW_PWD" | translate}}'
required
pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{7,}$"
name="newPassword"
[(ngModel)]="password"
#newPassInput="ngModel"
size="25"
(input)='handleValidation("newPassword", true)'
(focusout)='handleValidation("newPassword", false)'>
<span class="tooltip-content">
{{'TOOLTIP.PASSWORD' | translate}}
</span>
</label>
</div>
<div class="form-group">
<label for="reNewPassword">{{'CHANGE_PWD.CONFIRM_PWD' | translate}}</label>
<label for="reNewPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]='getValidationState("reNewPassword") === false'>
<input type="password" id="reNewPassword" placeholder='{{"PLACEHOLDER.CONFIRM_PWD" | translate}}'
required
pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{7,}$"
name="reNewPassword"
[(ngModel)]="ngModel"