Fix UI/UX issues:

Use blur event to instead focusout
Fix mail/ldap server testing spinner and button disabling issues
Fix content area/more info margins and make alert as overlay
fix reset password form issues
fix reset password style issue
fix tootip display inssue on reset form
commit index.html change
This commit is contained in:
Steven Zou 2017-04-06 11:31:20 +08:00
parent 69ebc92f87
commit 8f0285b4cb
15 changed files with 99 additions and 64 deletions

View File

@ -1,5 +1,6 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Harbor</title>
@ -7,11 +8,20 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico?v=2">
</head>
<body>
<harbor-app>Loading...</harbor-app>
<script type="text/javascript" src="/static/inline.bundle.js"></script>
<script type="text/javascript" src="/static/scripts.bundle.js">
</script><script type="text/javascript" src="/static/styles.bundle.js">
</script><script type="text/javascript" src="/static/vendor.bundle.js"></script>
<script type="text/javascript" src="/static/main.bundle.js"></script></body>
</html>
<body style="overflow-y: hidden;">
<harbor-app>
<div class="spinner spinner-lg app-loading">
Loading...
</div>
</harbor-app>
<script type="text/javascript" src="/static/inline.bundle.js"></script>
<script type="text/javascript" src="/static/scripts.bundle.js">
</script>
<script type="text/javascript" src="/static/styles.bundle.js">
</script>
<script type="text/javascript" src="/static/vendor.bundle.js"></script>
<script type="text/javascript" src="/static/main.bundle.js"></script>
</body>
</html>

View File

@ -4,7 +4,7 @@
"description": "Harbor UI with Clarity",
"angular-cli": {},
"scripts": {
"start": "ng serve --ssl 1 --ssl-key --ssl-cert --host 0.0.0.0 --proxy-config proxy.config.json",
"start": "ng serve --ssl 1 --ssl-key ssl/server.key --ssl-cert ssl/server.crt --host 0.0.0.0 --proxy-config proxy.config.json",
"lint": "tslint \"src/**/*.ts\"",
"test": "ng test --single-run",
"pree2e": "webdriver-manager update",

View File

@ -16,7 +16,7 @@
email
id="account_settings_email" size="30"
(input)='handleValidation("account_settings_email", false)'
(focusout)='handleValidation("account_settings_email", true)'>
(blur)='handleValidation("account_settings_email", true)'>
<span class="tooltip-content">
{{emailTooltip | translate}}
</span>
@ -25,7 +25,7 @@
<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)'>
<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)' (blur)='handleValidation("account_settings_full_name", true)'>
<span class="tooltip-content">
{{'TOOLTIP.FULL_NAME' | translate}}
</span>

View File

@ -14,7 +14,7 @@
id="reset_pwd_email"
size="40"
(input)="handleValidation(true)"
(focusout)="handleValidation(false)">
(blur)="handleValidation(false)">
<span class="tooltip-content">
{{'TOOLTIP.EMAIL' | translate}}
</span>
@ -25,7 +25,7 @@
</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()">{{btnCancelCaption | translate}}</button>
<button type="button" class="btn" [class.btn-outline]="!isSuccess" [class.btn-primary]="isSuccess" (click)="close()">{{btnCancelCaption | translate}}</button>
<button *ngIf="!isSuccess" type="button" class="btn btn-primary" [disabled]="!isValid || showProgress" (click)="send()">{{'BUTTON.SEND' | translate}}</button>
</div>
</clr-modal>

View File

@ -27,7 +27,7 @@
[(ngModel)]="newPwd"
#newPassInput="ngModel" size="42"
(input)='handleValidation("newPassword", false)'
(focusout)='handleValidation("newPassword", true)'>
(blur)='handleValidation("newPassword", true)'>
<span class="tooltip-content">
{{'TOOLTIP.PASSWORD' | translate}}
</span>
@ -44,7 +44,7 @@
[(ngModel)]="reNewPwd"
#reNewPassInput="ngModel" size="42"
(input)='handleValidation("reNewPassword", false)'
(focusout)='handleValidation("reNewPassword", true)'>
(blur)='handleValidation("reNewPassword", true)'>
<span class="tooltip-content">
{{'TOOLTIP.CONFIRM_PWD' | translate}}
</span>

View File

@ -1,12 +1,13 @@
<clr-modal [(clrModalOpen)]="opened" [clrModalStaticBackdrop]="true" [clrModalClosable]="false">
<h3 class="modal-title">{{'RESET_PWD.TITLE' | translate}}</h3>
<label class="modal-title reset-modal-title-override">{{'RESET_PWD.CAPTION2' | translate}}</label>
<inline-alert class="modal-title"></inline-alert>
<div class="modal-body" style="overflow-y: hidden;">
<form #resetPwdForm="ngForm" class="form">
<section class="form-block">
<div class="form-group">
<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'>
<label for="newPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]='!getValidationState("newPassword")'>
<input [disabled]="resetOk" type="password" id="newPassword" placeholder='{{"PLACEHOLDER.NEW_PWD" | translate}}'
required
pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{8,20}$"
@ -14,8 +15,8 @@
[(ngModel)]="password"
#newPassInput="ngModel"
size="25"
(input)='handleValidation("newPassword", true)'
(focusout)='handleValidation("newPassword", false)'>
(input)='handleValidation("newPassword", false)'
(blur)='handleValidation("newPassword", true)'>
<span class="tooltip-content">
{{'TOOLTIP.PASSWORD' | translate}}
</span>
@ -23,7 +24,7 @@
</div>
<div class="form-group">
<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'>
<label for="reNewPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-left" [class.invalid]='!getValidationState("reNewPassword")'>
<input [disabled]="resetOk" type="password" id="reNewPassword" placeholder='{{"PLACEHOLDER.CONFIRM_PWD" | translate}}'
required
pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{8,20}$"
@ -31,21 +32,19 @@
[(ngModel)]="confirmPwd"
#reNewPassInput
size="25"
(input)='handleValidation("reNewPassword", true)'
(focusout)='handleValidation("reNewPassword", false)'>
(input)='handleValidation("reNewPassword", false)'
(blur)='handleValidation("reNewPassword", true)'>
<span class="tooltip-content">
{{'TOOLTIP.CONFIRM_PWD' | translate}}
</span>
</label>
</div>
</section>
<inline-alert></inline-alert>
<div style="height: 30px;"></div>
</form>
</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()">{{btnCancelCaption | translate}}</button>
<button type="button" class="btn" [class.btn-outline]="!resetOk" [class.btn-primary]="resetOk" (click)="close()">{{btnCancelCaption | translate}}</button>
<button *ngIf="!resetOk" type="button" class="btn btn-primary" [disabled]="!isValid || showProgress" (click)="send()">{{'BUTTON.OK' | translate}}</button>
</div>
</clr-modal>

View File

@ -56,8 +56,7 @@ export class ResetPasswordComponent implements OnInit {
public getValidationState(key: string): boolean {
return this.validationState &&
this.validationState[key] &&
key === 'reNewPassword' ? this.samePassword() : true;
this.validationState[key];
}
public open(): void {
@ -109,10 +108,8 @@ export class ResetPasswordComponent implements OnInit {
}
public handleValidation(key: string, flag: boolean): void {
if (flag) {
if (!this.validationState[key]) {
this.validationState[key] = true;
}
if (!flag) {
this.validationState[key] = true;
} else {
this.validationState[key] = this.getControlValidationState(key);
if (this.validationState[key]) {

View File

@ -38,6 +38,7 @@
.more-info-link {
position: relative;
top: 90px;
left: 330px;
top: 80px;
left: 294px;
padding-right: 36px;
}

View File

@ -13,5 +13,15 @@
}
.content-area-override {
padding: 24px 36px !important;
padding: 36px !important;
padding-right: 21px !important;
}
.global-message-alert {
position: fixed;
top: 55px;
left: 260px;
width: 100%;
z-index: 999;
padding-right: 276px;
}

View File

@ -3,7 +3,9 @@
<navigator (showAccountSettingsModal)="openModal($event)" (showPwdChangeModal)="openModal($event)"></navigator>
<div class="content-container">
<div class="content-area" [class.container-override]="showSearch" [class.content-area-override]="!shouldOverrideContent" [class.start-content-padding]="shouldOverrideContent">
<global-message [isAppLevel]="false"></global-message>
<div class="global-message-alert">
<global-message [isAppLevel]="false"></global-message>
</div>
<!-- Only appear when searching -->
<search-result></search-result>
<router-outlet></router-outlet>

View File

@ -65,7 +65,8 @@
<button type="button" class="btn btn-outline" (click)="cancel()" [disabled]="!isValid() || !hasChanges()">{{'BUTTON.CANCEL' | translate}}</button>
<button type="button" class="btn btn-outline" (click)="testMailServer()" *ngIf="showTestServerBtn" [disabled]="!isMailConfigValid()">{{'BUTTON.TEST_MAIL' | translate}}</button>
<button type="button" class="btn btn-outline" (click)="testLDAPServer()" *ngIf="showLdapServerBtn" [disabled]="!isLDAPConfigValid()">{{'BUTTON.TEST_LDAP' | translate}}</button>
<span class="spinner spinner-inline" [hidden]="!testingInProgress"></span>
<span id="forTestingMail" class="spinner spinner-inline" [hidden]="hideMailTestingSpinner"></span>
<span id="forTestingLDAP" class="spinner spinner-inline" [hidden]="hideLDAPTestingSpinner"></span>
</div>
</div>
</div>

View File

@ -17,7 +17,7 @@ import { AppConfigService } from '../app-config.service';
import { SessionService } from '../shared/session.service';
import { MessageHandlerService } from '../shared/message-handler/message-handler.service';
const fakePass = "fakepassword";
const fakePass = "aWpLOSYkIzJTTU4wMDkx";
const TabLinkContentMap = {
"config-auth": "authentication",
"config-replication": "replication",
@ -36,7 +36,8 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
private currentTabId: string = "config-auth";//default tab
private originalCopy: Configuration;
private confirmSub: Subscription;
private testingOnGoing: boolean = false;
private testingMailOnGoing: boolean = false;
private testingLDAPOnGoing: boolean = false;
@ViewChild("repoConfigFrom") repoConfigForm: NgForm;
@ViewChild("systemConfigFrom") systemConfigForm: NgForm;
@ -131,10 +132,6 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
return this.onGoing;
}
public get testingInProgress(): boolean {
return this.testingOnGoing;
}
public isValid(): boolean {
return this.repoConfigForm &&
this.repoConfigForm.valid &&
@ -152,7 +149,8 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
public isMailConfigValid(): boolean {
return this.mailConfig &&
this.mailConfig.isValid();
this.mailConfig.isValid() &&
!this.testingMailOnGoing;
}
public get showTestServerBtn(): boolean {
@ -165,8 +163,18 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
this.allConfig.auth_mode.value === "ldap_auth";
}
public get hideMailTestingSpinner(): boolean {
return !this.testingMailOnGoing || !this.showTestServerBtn;
}
public get hideLDAPTestingSpinner(): boolean {
return !this.testingLDAPOnGoing || !this.showLdapServerBtn;
}
public isLDAPConfigValid(): boolean {
return this.authConfig && this.authConfig.isValid();
return this.authConfig &&
this.authConfig.isValid() &&
!this.testingLDAPOnGoing;
}
public tabLinkClick(tabLink: string) {
@ -239,6 +247,9 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
* @memberOf ConfigurationComponent
*/
public testMailServer(): void {
if(this.testingMailOnGoing){
return;//Should not come here
}
let mailSettings = {};
for (let prop in this.allConfig) {
if (prop.startsWith("email_")) {
@ -255,23 +266,27 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
delete mailSettings["email_password"];
}
this.testingOnGoing = true;
this.testingMailOnGoing = true;
this.configService.testMailServer(mailSettings)
.then(response => {
this.testingOnGoing = false;
this.testingMailOnGoing = false;
this.msgHandler.showSuccess("CONFIG.TEST_MAIL_SUCCESS");
})
.catch(error => {
this.testingOnGoing = false;
this.testingMailOnGoing = false;
let err = error._body;
if(!err){
if (!err) {
err = "UNKNOWN";
}
this.msgHandler.showError("CONFIG.TEST_MAIL_FAILED", {'param': err});
this.msgHandler.showError("CONFIG.TEST_MAIL_FAILED", { 'param': err });
});
}
public testLDAPServer(): void {
if(this.testingLDAPOnGoing){
return;//Should not come here
}
let ldapSettings = {};
for (let prop in this.allConfig) {
if (prop.startsWith("ldap_")) {
@ -281,25 +296,25 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
let allChanges = this.getChanges();
let ldapSearchPwd = allChanges["ldap_search_password"];
if(ldapSearchPwd){
if (ldapSearchPwd) {
ldapSettings['ldap_search_password'] = ldapSearchPwd;
}else{
} else {
delete ldapSettings['ldap_search_password'];
}
this.testingOnGoing = true;
this.testingLDAPOnGoing = true;
this.configService.testLDAPServer(ldapSettings)
.then(respone => {
this.testingOnGoing = false;
this.testingLDAPOnGoing = false;
this.msgHandler.showSuccess("CONFIG.TEST_LDAP_SUCCESS");
})
.catch(error => {
this.testingOnGoing = false;
this.testingLDAPOnGoing = false;
let err = error._body;
if(!err){
if (!err) {
err = "UNKNOWN";
}
this.msgHandler.showError("CONFIG.TEST_LDAP_FAILED", {'param': err});
this.msgHandler.showError("CONFIG.TEST_LDAP_FAILED", { 'param': err });
});
}

View File

@ -53,7 +53,7 @@ export class MessageComponent implements OnInit, OnDestroy {
// Make the message alert bar dismiss after several intervals.
//Only for this case
this.timer = setTimeout(() => this.onClose(), dismissInterval);
//this.timer = setTimeout(() => this.onClose(), dismissInterval);
}
);
}

View File

@ -1,6 +1,6 @@
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="row flex-items-xs-between flex-items-xs-top">
<div class="row flex-items-xs-between flex-items-xs-top" style="padding-left: 15px; padding-right: 15px;">
<h2 class="header-title">{{'PROJECT.PROJECTS' | translate}}</h2>
<div>
<statistics-panel></statistics-panel>
@ -13,7 +13,7 @@
</div>
<div class="option-right">
<div class="select" style="float: left;">
<select (change)="doFilterProjects($event)">
<select (change)="doFilterProjects($event)">
<option value="0">{{projectTypes[0] | translate}}</option>
<option value="1">{{projectTypes[1] | translate}}</option>
</select>

View File

@ -6,7 +6,7 @@
<label for="username" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]='getValidationState("username")'>
<input type="text" required pattern='[^"~#$%]+' maxLengthExt="20" #usernameInput="ngModel" name="username" [(ngModel)]="newUser.username" id="username" size="36"
(input)='handleValidation("username", false)'
(focusout)='handleValidation("username", true)'>
(blur)='handleValidation("username", true)'>
<span class="tooltip-content">
{{usernameTooltip | translate}}
</span>
@ -20,7 +20,7 @@
email
id="email" size="36"
(input)='handleValidation("email", false)'
(focusout)='handleValidation("email", true)'>
(blur)='handleValidation("email", true)'>
<span class="tooltip-content">
{{emailTooltip | translate}}
</span>
@ -33,7 +33,7 @@
<label for="realname" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]='getValidationState("realname")'>
<input type="text" name="realname" #fullNameInput="ngModel" [(ngModel)]="newUser.realname" required maxLengthExt="20" id="realname" size="36"
(input)='handleValidation("realname", false)'
(focusout)='handleValidation("realname", true)'>
(blur)='handleValidation("realname", true)'>
<span class="tooltip-content">
{{'TOOLTIP.FULL_NAME' | translate}}
</span>
@ -49,7 +49,7 @@
[(ngModel)]="newUser.password"
#newPassInput="ngModel" size="36"
(input)='handleValidation("newPassword", false)'
(focusout)='handleValidation("newPassword", true)'>
(blur)='handleValidation("newPassword", true)'>
<span class="tooltip-content">
{{'TOOLTIP.PASSWORD' | translate}}
</span>
@ -66,7 +66,7 @@
[(ngModel)]="confirmedPwd"
#confirmPassInput="ngModel" size="36"
(input)='handleValidation("confirmPassword", false)'
(focusout)='handleValidation("confirmPassword", true)'>
(blur)='handleValidation("confirmPassword", true)'>
<span class="tooltip-content">
{{'TOOLTIP.CONFIRM_PWD' | translate}}
</span>
@ -77,7 +77,7 @@
<label for="comment" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]='getValidationState("comment")'>
<input type="text" #commentInput="ngModel" name="comment" [(ngModel)]="newUser.comment" maxLengthExt="20" id="comment" size="36"
(input)='handleValidation("comment", false)'
(focusout)='handleValidation("comment", true)'>
(blur)='handleValidation("comment", true)'>
<span class="tooltip-content">
{{'TOOLTIP.COMMENT' | translate}}
</span>