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

update ui code for fixing bugs and upgrade Clarity version to 0.8.7

parent 29f3e609
......@@ -4,7 +4,7 @@
"description": "Angular-CLI starter for a Clarity project",
"angular-cli": {},
"scripts": {
"start": "ng serve --host 0.0.0.0",
"start": "ng serve --host 0.0.0.0 --proxy-config proxy.config.json",
"lint": "tslint \"src/**/*.ts\"",
"test": "ng test --single-run",
"pree2e": "webdriver-manager update",
......@@ -24,9 +24,9 @@
"@ngx-translate/http-loader": "0.0.3",
"@webcomponents/custom-elements": "1.0.0-alpha.3",
"angular2-cookie": "^1.2.6",
"clarity-angular": "^0.8.0",
"clarity-icons": "^0.8.0",
"clarity-ui": "^0.8.0",
"clarity-angular": "0.8.7",
"clarity-icons": "0.8.7",
"clarity-ui": "0.8.7",
"core-js": "^2.4.1",
"fs": "0.0.1-security",
"mutationobserver-shim": "^0.3.2",
......
{
"/api/*": {
"target": "http://10.117.4.165",
"secure": false,
"logLevel": "debug"
},
"/service/*": {
"target": "http://10.117.4.165",
"secure": false,
"logLevel": "debug"
},
"/login": {
"target": "http://10.117.4.165",
"secure": false,
"logLevel": "debug"
},
"/sign_in": {
"target": "http://10.117.4.165",
"secure": false,
"logLevel": "debug"
},
"/log_out": {
"target": "http://10.117.4.165",
"secure": false,
"logLevel": "debug"
},
"/sendEmail": {
"target": "http://10.117.4.165",
"secure": false,
"logLevel": "debug"
},
"/language": {
"target": "http://10.117.4.165",
"secure": false,
"logLevel": "debug"
},
"/reset": {
"target": "http://10.117.4.165",
"secure": false,
"logLevel": "debug"
}
}
\ No newline at end of file
<clr-modal [(clrModalOpen)]="opened" [clrModalStaticBackdrop]="staticBackdrop">
<h3 class="modal-title">{{'PROFILE.TITLE' | translate}}</h3>
<inline-alert class="modal-title" (confirmEvt)="confirmCancel($event)"></inline-alert>
<div class="modal-body" style="overflow-y: hidden;">
<form #accountSettingsFrom="ngForm" class="form">
<section class="form-block">
<div class="form-group">
<label for="account_settings_username" class="col-md-4">{{'PROFILE.USER_NAME' | translate}}</label>
<input type="text" name="account_settings_username" [(ngModel)]="account.username" disabled id="account_settings_username" size="31">
<div class="form-group form-group-override">
<label for="account_settings_username" class="form-group-label-override">{{'PROFILE.USER_NAME' | translate}}</label>
<input type="text" name="account_settings_username" [(ngModel)]="account.username" disabled id="account_settings_username" size="33">
</div>
<div class="form-group">
<label for="account_settings_email" class="col-md-4 required">{{'PROFILE.EMAIL' | translate}}</label>
<label for="account_settings_email" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-left" [class.invalid]="eamilInput.invalid && (eamilInput.dirty || eamilInput.touched)">
<div class="form-group form-group-override">
<label for="account_settings_email" class="required form-group-label-override">{{'PROFILE.EMAIL' | translate}}</label>
<label for="account_settings_email" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-left" [class.invalid]='!getValidationState("account_settings_email")'>
<input name="account_settings_email" type="text" #eamilInput="ngModel" [(ngModel)]="account.email"
required
pattern='^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$' id="account_settings_email" size="28">
pattern='^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$' id="account_settings_email" size="30"
(input)='handleValidation("account_settings_email", false)'
(focusout)='handleValidation("account_settings_email", true)'>
<span class="tooltip-content">
{{'TOOLTIP.EMAIL' | translate}}
{{emailTooltip | translate}}
</span>
</label>
</label><span class="spinner spinner-inline" [hidden]="!checkProgress"></span>
</div>
<div class="form-group">
<label for="account_settings_full_name" class="col-md-4 required">{{'PROFILE.FULL_NAME' | translate}}</label>
<label for="account_settings_full_name" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-left" [class.invalid]="fullNameInput.invalid && (fullNameInput.dirty || fullNameInput.touched)">
<input type="text" name="account_settings_full_name" #fullNameInput="ngModel" [(ngModel)]="account.realname" required maxLengthExt="20" id="account_settings_full_name" size="28">
<div class="form-group form-group-override">
<label for="account_settings_full_name" class="required form-group-label-override">{{'PROFILE.FULL_NAME' | translate}}</label>
<label for="account_settings_full_name" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-left" [class.invalid]='!getValidationState("account_settings_full_name")'>
<input type="text" name="account_settings_full_name" #fullNameInput="ngModel" [(ngModel)]="account.realname" required maxLengthExt="20" id="account_settings_full_name" size="30" (input)='handleValidation("account_settings_full_name", false)' (focusout)='handleValidation("account_settings_full_name", true)'>
<span class="tooltip-content">
{{'TOOLTIP.FULL_NAME' | translate}}
</span>
</label>
</div>
<div class="form-group">
<label for="account_settings_comments" class="col-md-4">{{'PROFILE.COMMENT' | translate}}</label>
<div class="form-group form-group-override">
<label for="account_settings_comments" class="form-group-label-override">{{'PROFILE.COMMENT' | translate}}</label>
<label for="account_settings_comments" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-left" [class.invalid]="commentInput.invalid && (commentInput.dirty || commentInput.touched)">
<input type="text" #commentInput="ngModel" maxLengthExt="20" name="account_settings_comments" [(ngModel)]="account.comment" id="account_settings_comments" size="28">
<input type="text" #commentInput="ngModel" maxLengthExt="20" name="account_settings_comments" [(ngModel)]="account.comment" id="account_settings_comments" size="30">
<span class="tooltip-content">
{{'TOOLTIP.COMMENT' | translate}}
</span>
......@@ -38,7 +41,6 @@
</div>
</section>
</form>
<inline-alert (confirmEvt)="confirmCancel($event)"></inline-alert>
</div>
<div class="modal-footer">
<span class="spinner spinner-inline" style="top:8px;" [hidden]="showProgress === false"></span>
......
......@@ -10,7 +10,8 @@ import { InlineAlertComponent } from '../../shared/inline-alert/inline-alert.com
@Component({
selector: "account-settings-modal",
templateUrl: "account-settings-modal.component.html"
templateUrl: "account-settings-modal.component.html",
styleUrls: ['../../common.css']
})
export class AccountSettingsModalComponent implements OnInit, AfterViewChecked {
......@@ -19,9 +20,16 @@ export class AccountSettingsModalComponent implements OnInit, AfterViewChecked {
account: SessionUser;
error: any = null;
originalStaticData: SessionUser;
private emailTooltip: string = 'TOOLTIP.EMAIL';
private validationStateMap: any = {
"account_settings_email": true,
"account_settings_full_name": true
};
private mailAlreadyChecked = {};
private isOnCalling: boolean = false;
private formValueChanged: boolean = false;
private checkOnGoing: boolean = false;
accountFormRef: NgForm;
@ViewChild("accountSettingsFrom") accountForm: NgForm;
......@@ -37,6 +45,54 @@ export class AccountSettingsModalComponent implements OnInit, AfterViewChecked {
this.account = Object.assign({}, this.session.getCurrentUser());
}
private getValidationState(key: string): boolean {
return this.validationStateMap[key];
}
private handleValidation(key: string, flag: boolean): void {
if (flag) {
//Checking
let cont = this.accountForm.controls[key];
if (cont) {
this.validationStateMap[key] = cont.valid;
//Check email existing from backend
if (cont.valid && key === "account_settings_email") {
if (this.formValueChanged && this.account.email != this.originalStaticData.email) {
if (this.mailAlreadyChecked[this.account.email]) {
this.validationStateMap[key] = !this.mailAlreadyChecked[this.account.email].result;
if (!this.validationStateMap[key]) {
this.emailTooltip = "TOOLTIP.EMAIL_EXISTING";
}
return;
}
//Mail changed
this.checkOnGoing = true;
this.session.checkUserExisting("email", this.account.email)
.then((res: boolean) => {
this.checkOnGoing = false;
this.validationStateMap[key] = !res;
if (res) {
this.emailTooltip = "TOOLTIP.EMAIL_EXISTING";
}
this.mailAlreadyChecked[this.account.email] = {
result: res
}; //Tag it checked
})
.catch(error => {
this.checkOnGoing = false;
this.validationStateMap[key] = false;//Not valid @ backend
});
}
}
}
} else {
//Reset
this.validationStateMap[key] = true;
this.emailTooltip = "TOOLTIP.EMAIL";
}
}
private isUserDataChange(): boolean {
if (!this.originalStaticData || !this.account) {
return false;
......@@ -56,13 +112,20 @@ export class AccountSettingsModalComponent implements OnInit, AfterViewChecked {
}
public get isValid(): boolean {
return this.accountForm && this.accountForm.valid && this.error === null;
return this.accountForm &&
this.accountForm.valid &&
this.error === null &&
this.validationStateMap["account_settings_email"]; //backend check is valid as well
}
public get showProgress(): boolean {
return this.isOnCalling;
}
public get checkProgress(): boolean {
return this.checkOnGoing;
}
ngAfterViewChecked(): void {
if (this.accountFormRef != this.accountForm) {
this.accountFormRef = this.accountForm;
......@@ -124,9 +187,9 @@ export class AccountSettingsModalComponent implements OnInit, AfterViewChecked {
.catch(error => {
this.isOnCalling = false;
this.error = error;
if(accessErrorHandler(error, this.msgService)){
if (accessErrorHandler(error, this.msgService)) {
this.opened = false;
}else{
} else {
this.inlineAlert.showInlineError(error);
}
});
......
......@@ -9,14 +9,17 @@ 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 { SignUpPageComponent } from './sign-up/sign-up-page.component';
import { PasswordSettingService } from './password/password-setting.service';
import { RepositoryModule } from '../repository/repository.module';
@NgModule({
imports: [
CoreModule,
RouterModule,
SharedModule
SharedModule,
RepositoryModule
],
declarations: [
SignInComponent,
......@@ -24,12 +27,14 @@ import { PasswordSettingService } from './password/password-setting.service';
AccountSettingsModalComponent,
SignUpComponent,
ForgotPasswordComponent,
ResetPasswordComponent],
ResetPasswordComponent,
SignUpPageComponent],
exports: [
SignInComponent,
PasswordSettingComponent,
AccountSettingsModalComponent,
ResetPasswordComponent],
ResetPasswordComponent,
SignUpPageComponent],
providers: [PasswordSettingService]
})
......
<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;">
<inline-alert class="modal-title"></inline-alert>
<div class="modal-body" style="overflow-y: hidden; min-height: 130px;">
<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>
<div class="form-group form-group-override">
<label for="reset_pwd_email" class="required form-group-label-override">{{'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"
size="40"
(input)="handleValidation(true)"
(focusout)="handleValidation(false)">
<span class="tooltip-content">
......@@ -21,8 +22,6 @@
</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>
......
......@@ -8,7 +8,7 @@ import { InlineAlertComponent } from '../../shared/inline-alert/inline-alert.com
@Component({
selector: 'forgot-password',
templateUrl: "forgot-password.component.html",
styleUrls: ['password.component.css']
styleUrls: ['password.component.css', '../../common.css']
})
export class ForgotPasswordComponent {
opened: boolean = false;
......
<clr-modal [(clrModalOpen)]="opened" [clrModalStaticBackdrop]="true">
<h3 class="modal-title">{{'CHANGE_PWD.TITLE' | translate}}</h3>
<div class="modal-body" style="min-height: 250px; overflow-y: hidden;">
<inline-alert class="modal-title" (confirmEvt)="confirmCancel($event)"></inline-alert>
<div class="modal-body" style="overflow-y: hidden;">
<form #changepwdForm="ngForm" class="form">
<section class="form-block">
<div class="form-group">
<label for="oldPassword">{{'CHANGE_PWD.CURRENT_PWD' | translate}}</label>
<div class="form-group form-group-override">
<label for="oldPassword" class="required form-group-label-override">{{'CHANGE_PWD.CURRENT_PWD' | translate}}</label>
<label for="oldPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]="oldPassInput.invalid && (oldPassInput.dirty || oldPassInput.touched)">
<input type="password" id="oldPassword" placeholder='{{"PLACEHOLDER.CURRENT_PWD" | translate}}'
required
name="oldPassword"
[(ngModel)]="oldPwd"
#oldPassInput="ngModel" size="25">
#oldPassInput="ngModel" size="30">
<span class="tooltip-content">
{{'TOOLTIP.CURRENT_PWD' | translate}}
</span>
</label>
</div>
<div class="form-group">
<label for="newPassword">{{'CHANGE_PWD.NEW_PWD' | translate}}</label>
<div class="form-group form-group-override">
<label for="newPassword" class="required form-group-label-override">{{'CHANGE_PWD.NEW_PWD' | translate}}</label>
<label for="newPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-left" [class.invalid]="newPassInput.invalid && (newPassInput.dirty || newPassInput.touched)">
<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)]="newPwd"
#newPassInput="ngModel" size="25">
#newPassInput="ngModel" size="30">
<span class="tooltip-content">
{{'TOOLTIP.PASSWORD' | translate}}
</span>
</label>
</div>
<div class="form-group">
<label for="reNewPassword">{{'CHANGE_PWD.CONFIRM_PWD' | translate}}</label>
<div class="form-group form-group-override">
<label for="reNewPassword" class="required form-group-label-override">{{'CHANGE_PWD.CONFIRM_PWD' | translate}}</label>
<label for="reNewPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-left" [class.invalid]="(reNewPassInput.invalid && (reNewPassInput.dirty || reNewPassInput.touched)) || (!newPassInput.invalid && reNewPassInput.value != newPassInput.value)">
<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)]="reNewPwd"
#reNewPassInput="ngModel" size="25">
#reNewPassInput="ngModel" size="30">
<span class="tooltip-content">
{{'TOOLTIP.CONFIRM_PWD' | translate}}
</span>
</label>
</div>
</section>
<inline-alert (confirmEvt)="confirmCancel($event)"></inline-alert>
</form>
</div>
<div class="modal-footer">
......
......@@ -11,7 +11,8 @@ import { InlineAlertComponent } from '../../shared/inline-alert/inline-alert.com
@Component({
selector: 'password-setting',
templateUrl: "password-setting.component.html"
templateUrl: "password-setting.component.html",
styleUrls: ['../../common.css']
})
export class PasswordSettingComponent implements AfterViewChecked {
opened: boolean = false;
......
.reset-modal-title-override {
font-size: 14px !important;
}
.form-group-override {
padding-left: 130px !important;
}
\ No newline at end of file
......@@ -5,7 +5,7 @@
<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" class="form-group-label-override">{{'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
......@@ -22,7 +22,7 @@
</label>
</div>
<div class="form-group">
<label for="reNewPassword">{{'CHANGE_PWD.CONFIRM_PWD' | translate}}</label>
<label for="reNewPassword" class="form-group-label-override">{{'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
......
......@@ -11,7 +11,7 @@ import { MessageService } from '../../global-message/message.service';
@Component({
selector: 'reset-password',
templateUrl: "reset-password.component.html",
styleUrls: ['password.component.css']
styleUrls: ['password.component.css', '../../common.css']
})
export class ResetPasswordComponent implements OnInit{
opened: boolean = true;
......
......@@ -12,4 +12,32 @@
font-size: 14px;
float: right;
top: -5px;
}
.popular-repo-wrapper {
background-color: white;
min-height: 100vh;
display: flex;
flex-grow: 1;
margin-top: 24px;
justify-content: center;
flex-direction: column;
height: auto;
position: relative;
margin-left: 1px;
}
.login-wrapper-override {
flex-wrap: wrap;
}
.repo-container {
width: 100%;
margin-top: -250px;
}
.more-info-link {
position: relative;
top: 100px;
left: 330px;
}
\ No newline at end of file
<div class="login-wrapper">
<div class="login-wrapper login-wrapper-override">
<form #signInForm="ngForm" class="login">
<label class="title">
VMware Harbor<span class="trademark">&#8482;</span>
......@@ -33,7 +33,13 @@
<button [disabled]="isOnGoing || !isValid" type="submit" class="btn btn-primary" (click)="signIn()">{{ 'BUTTON.LOG_IN' | translate }}</button>
<a href="javascript:void(0)" class="signup" (click)="signUp()" *ngIf="selfSignUp">{{ 'BUTTON.SIGN_UP_LINK' | translate }}</a>
</div>
<div>
<a href="https://github.com/vmware/harbor" target="_blank" class="more-info-link">{{ 'BUTTON.MORE_INFO' | translate }}</a>
</div>
</form>
<div id="pop_repo" class="popular-repo-wrapper">
<top-repo class="repo-container"></top-repo>
</div>
</div>
<sign-up #signupDialog></sign-up>
<sign-up #signupDialog (userCreation)="handleUserCreation($event)"></sign-up>
<forgot-password #forgotPwdDialog></forgot-password>
\ No newline at end of file
......@@ -7,11 +7,12 @@ import { SessionService } from '../../shared/session.service';
import { SignInCredential } from '../../shared/sign-in-credential';
import { SignUpComponent } from '../sign-up/sign-up.component';
import { harborRootRoute } from '../../shared/shared.const';
import { CommonRoutes } from '../../shared/shared.const';
import { ForgotPasswordComponent } from '../password/forgot-password.component';
import { AppConfigService } from '../../app-config.service';
import { AppConfig } from '../../app-config';
import { User } from '../../user/user';
//Define status flags for signing in states
export const signInStatusNormal = 0;
......@@ -105,6 +106,17 @@ export class SignInComponent implements AfterViewChecked, OnInit {
}
//Fill the new user info into the sign in form
private handleUserCreation(user: User): void {
if(user){
this.currentForm.setValue({
"login_username": user.username,
"login_password": user.password
});
}
}
//Implement interface
//Watch the view change only when view is in error state
ngAfterViewChecked() {
......@@ -139,7 +151,7 @@ export class SignInComponent implements AfterViewChecked, OnInit {
//Redirect to the right route
if (this.redirectUrl === "") {
//Routing to the default location
this.router.navigateByUrl(harborRootRoute);
this.router.navigateByUrl(CommonRoutes.HARBOR_DEFAULT);
} else {
this.router.navigateByUrl(this.redirectUrl);
}
......
<h3 class="modal-title">{{'SIGN_UP.TITLE' | translate}}</h3>
<new-user-form isSelfRegistration="true" (valueChange)="formValueChange($event)"></new-user-form>
<div>
<span class="spinner spinner-inline" style="top:8px;" [hidden]="!inProgress"> </span>
<button type="button" class="btn btn-outline" [disabled]="!canBeCancelled" (click)="cancel()">{{'BUTTON.CANCEL' | translate}}</button>
<button type="button" class="btn btn-primary" [disabled]="!isValid || inProgress" (click)="create()">{{ 'BUTTON.SIGN_UP' | translate }}</button>
</div>
\ No newline at end of file
import { Component, Output, ViewChild, OnInit } from '@angular/core';
import { NgForm } from '@angular/forms';
import { Router } from '@angular/router';
import { NewUserFormComponent } from '../../shared/new-user-form/new-user-form.component';
import { User } from '../../user/user';
import { UserService } from '../../user/user.service';
import { errorHandler } from '../../shared/shared.utils';
import { AlertType } from '../../shared/shared.const';
import { MessageService } from '../../global-message/message.service';
@Component({
selector: 'sign-up-page',
templateUrl: "sign-up-page.component.html"
})
export class SignUpPageComponent implements OnInit {
private error: any;
private onGoing: boolean = false;
private formValueChanged: boolean = false;
constructor(
private userService: UserService,
private msgService: MessageService,
private router: Router) { }
@ViewChild(NewUserFormComponent)
private newUserForm: NewUserFormComponent;
private getNewUser(): User {
return this.newUserForm.getData();