diff --git a/src/ui_ng/package.json b/src/ui_ng/package.json index 542105af1..f8f8595a6 100644 --- a/src/ui_ng/package.json +++ b/src/ui_ng/package.json @@ -29,6 +29,7 @@ "clarity-ui": "0.8.7", "core-js": "^2.4.1", "fs": "0.0.1-security", + "jquery": "^2.2.4", "mutationobserver-shim": "^0.3.2", "rxjs": "^5.0.1", "ts-helpers": "^1.1.1", @@ -37,10 +38,10 @@ }, "devDependencies": { "@angular/compiler-cli": "^2.4.1", + "@angular/cli": "^1.0.0", "@types/core-js": "^0.9.34", "@types/jasmine": "^2.2.30", "@types/node": "^6.0.42", - "angular-cli": "^1.0.0-beta.24", "bootstrap": "4.0.0-alpha.5", "codelyzer": "~1.0.0-beta.3", "enhanced-resolve": "^3.0.0", diff --git a/src/ui_ng/src/app/account/password/forgot-password.component.html b/src/ui_ng/src/app/account/password/forgot-password.component.html index d97037710..e0f4c1f11 100644 --- a/src/ui_ng/src/app/account/password/forgot-password.component.html +++ b/src/ui_ng/src/app/account/password/forgot-password.component.html @@ -7,10 +7,10 @@
- - + {{'CONFIG.TOOLTIP.LDAP_SEARCH_DN' | translate}}
@@ -68,7 +68,7 @@ - + {{'CONFIG.TOOLTIP.LDAP_BASE_DN' | translate}} @@ -95,7 +95,7 @@ - + {{'CONFIG.TOOLTIP.LDAP_UID' | translate}} @@ -109,7 +109,7 @@ - + {{'CONFIG.TOOLTIP.LDAP_SCOPE' | translate}} @@ -124,7 +124,7 @@ - + {{'CONFIG.TOOLTIP.PRO_CREATION_RESTRICTION' | translate}} @@ -132,7 +132,7 @@ - + {{'CONFIG.TOOLTIP.SELF_REGISTRATION' | translate}} diff --git a/src/ui_ng/src/app/config/config.component.html b/src/ui_ng/src/app/config/config.component.html index 7cfc10599..5616d5459 100644 --- a/src/ui_ng/src/app/config/config.component.html +++ b/src/ui_ng/src/app/config/config.component.html @@ -1,48 +1,48 @@
-
-

{{'CONFIG.TITLE' | translate }}

- - -
- -
-
-
-
-
- - - - - {{'CONFIG.TOOLTIP.VERIFY_REMOTE_CERT' | translate }} - - -
-
-
-
-
- -
-
-
-
-
- -
+ +
+
+ + + + + +
-
\ No newline at end of file diff --git a/src/ui_ng/src/app/config/email/config-email.component.html b/src/ui_ng/src/app/config/email/config-email.component.html index f00bb96d6..845f5cb40 100644 --- a/src/ui_ng/src/app/config/email/config-email.component.html +++ b/src/ui_ng/src/app/config/email/config-email.component.html @@ -63,7 +63,7 @@ - + {{'CONFIG.SSL_TOOLTIP' | translate}} diff --git a/src/ui_ng/src/app/shared/gauge/gauge.component.css b/src/ui_ng/src/app/shared/gauge/gauge.component.css new file mode 100644 index 000000000..3eda1b1d0 --- /dev/null +++ b/src/ui_ng/src/app/shared/gauge/gauge.component.css @@ -0,0 +1,284 @@ +.esxc-gauge-container { + position: relative; + overflow: hidden; +} + + +/* + * Gauge track color + * + * TODO: we should make this configurable in the directive + */ + +.esxc-gauge-circle-bg { + background-color: #EEE; +} + + +/* + * Gauge bar + */ + +.esxc-gauge-circle-fill { + position: absolute; + top: 0; + left: 0; + right: 0; + background: transparent; + transform: rotate(0deg); +} + + +/* + * Gauge center area + */ + +.esxc-gauge-circle-inner { + position: absolute; + top: 0; + left: 0; +} + + +/* + * Gauge caption + */ + +.esxc-gauge-circle-caption { + text-align: center; +} + +.esxc-gauge-bar-one { + opacity: 0.5; +} + + +/* + * Small size gauge sizing + */ + +.esxc-gauge-small .esxc-gauge-container { + width: 100px; + height: 50px; +} + +.esxc-gauge-small .esxc-gauge-circle-fill { + height: 100px; + border-radius: 50px; + clip: rect(50px, 100px, 100px, 0); +} + +.esxc-gauge-small .esxc-gauge-circle-bg { + height: 100px; + border-radius: 50px; +} + +.esxc-gauge-small .esxc-gauge-circle-inner { + width: 80px; + height: 80px; + border-radius: 40px; + margin: 10px; +} + +.esxc-gauge-small .esxc-gauge-circle-caption { + margin-top: 20px; +} + + +/* + * Medium size gauge sizing + */ + +.esxc-gauge-medium .esxc-gauge-container { + width: 130px; + height: 65px; +} + +.esxc-gauge-medium .esxc-gauge-circle-fill { + height: 130px; + border-radius: 130px; + clip: rect(65px, 130px, 130px, 0); +} + +.esxc-gauge-medium .esxc-gauge-circle-bg { + height: 130px; + border-radius: 65px; +} + +.esxc-gauge-medium .esxc-gauge-circle-inner { + height: 120px; + width: 120px; + border-radius: 60px; + margin: 5px; +} + + +/* + * Large size gauge sizing + */ + +.esxc-gauge-large .esxc-gauge-container { + width: 260px; + height: 130px; +} + +.esxc-gauge-large .esxc-gauge-circle-fill { + height: 260px; + border-radius: 130px; + clip: rect(130px, 260px, 260px, 0); +} + +.esxc-gauge-large .esxc-gauge-circle-bg { + height: 260px; + border-radius: 130px; +} + +.esxc-gauge-large .esxc-gauge-circle-inner { + width: 240px; + height: 240px; + border-radius: 120px; + margin: 10px; +} + +.esxc-gauge-large .esxc-gauge-circle-caption { + margin-top: 95px; +} + +.esxc-gauge-small .esxc-gauge-circle-caption .esxc-value { + font-size: 24px; + font-weight: bold; +} + +.esxc-gauge-small .esxc-gauge-circle-caption .esxc-unit { + font-size: 10px; +} + +.esxc-gauge-small .esxc-gauge-circle-caption .esxc-loading { + font-size: 12px; +} + +.esxc-gauge-small .esxc-title { + font-size: 16px; + text-align: center; + margin-top: 3px; +} + +.esxc-gauge-small .esxc-title .esxc-bar-title { + float: left; + text-align: right; + width: 70px; + margin-top: 0; +} + +.esxc-gauge-small .esxc-limit { + text-align: center; +} + +.esxc-gauge-small .esxc-limit .esxc-value { + font-size: 10px; +} + +.esxc-gauge-small .esxc-limit .esxc-unit { + font-size: 10px; +} + +.esxc-gauge-small .esxc-limit .esxc-label { + font-size: 10px; +} + +.esxc-gauge-small .esxc-limit .esxc-bar-limit { + text-align: right; +} + +.esxc-gauge-medium .esxc-gauge-circle-caption { + margin-top: 40px; + color: #000; +} + +.esxc-gauge-medium .esxc-gauge-circle-caption .esxc-value { + font-size: 22px; +} + +.esxc-gauge-medium .esxc-gauge-circle-caption .esxc-unit { + font-size: 14px; +} + +.esxc-gauge-medium .esxc-gauge-circle-caption .esxc-loading { + font-size: 25px; +} + +.esxc-gauge-medium .esxc-limit { + text-align: center; + color: #565656; +} + +.esxc-gauge-medium .esxc-limit .esxc-value { + font-size: 12px; +} + +.esxc-gauge-medium .esxc-limit .esxc-unit { + font-size: 12px; +} + +.esxc-gauge-medium .esxc-limit .esxc-label { + font-size: 12px; +} + +.esxc-gauge-medium .esxc-title { + font-size: 13px; + font-weight: 600; + color: #565656; + text-align: center; + margin-top: -8px; +} + +.esxc-gauge-large .esxc-gauge-circle-caption .esxc-value { + font-size: 40px; +} + +.esxc-gauge-large .esxc-gauge-circle-caption .esxc-unit { + font-size: 18px; +} + +.esxc-gauge-large .esxc-gauge-circle-caption .esxc-loading { + font-size: 30px; +} + +.esxc-gauge-large .esxc-limit { + text-align: center; + margin-top: 4px; +} + +.esxc-gauge-large .esxc-limit .esxc-value { + font-size: 14px; + color: #565656; + line-height: 18px; +} + +.esxc-gauge-large .esxc-limit .esxc-unit { + font-size: 14px; +} + +.esxc-gauge-large .esxc-limit .esxc-label { + font-size: 14px; +} + +.esxc-gauge-large .esxc-limit .esxc-bar-limit { + text-align: right; +} + +.esxc-gauge-large .esxc-title { + font-size: 18px; + font-weight: 600; + color: #565656; + line-height: 18px; + text-align: center; + margin-top: -2px; +} + +.esxc-gauge-large .esxc-title .esxc-bar-title { + float: left; + text-align: right; + width: 70px; + margin-top: 0; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/gauge/gauge.component.html b/src/ui_ng/src/app/shared/gauge/gauge.component.html new file mode 100644 index 000000000..372bf8f41 --- /dev/null +++ b/src/ui_ng/src/app/shared/gauge/gauge.component.html @@ -0,0 +1,18 @@ +
+
+
+
+
+
+
+ {{used}} +
+
+
+
+ {{threasHold}} + GB + {{'STATISTICS.LIMIT' | translate}} +
+
{{title | translate}}
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/shared/gauge/gauge.component.ts b/src/ui_ng/src/app/shared/gauge/gauge.component.ts new file mode 100644 index 000000000..cdde6616c --- /dev/null +++ b/src/ui_ng/src/app/shared/gauge/gauge.component.ts @@ -0,0 +1,272 @@ +import { + Component, + Input, + AfterViewInit, + ViewChild, + ElementRef +} from '@angular/core'; + +import * as $ from 'jquery'; + +const RESOURCE_COLOR_GREEN_NORMAL: string = '#5DB700'; +const RESOURCE_COLOR_ORANGE_NORMAL: string = '#FBBF00'; +const RESOURCE_COLOR_RED_NORMAL: string = '#EA400D'; +const RESOURCE_COLOR_GREY500: string = '#D7DEE2'; +const RESOURCE_COLOR_GREY600: string = '#C7D1D6'; + +/** + * Guage to visualize percent usage. + */ +@Component({ + selector: 'esxc-gauge', + templateUrl: 'gauge.component.html', + styleUrls: ['gauge.component.css'] +}) + +export class GaugeComponent implements AfterViewInit { + private _backgroundColor: string; + private _colorOne: string; + private _colorTwo: string; + private _size: string = "small"; //Support small, medium, large + private _title: string = "UNKNOWN"; //Lang key + private _used: number = 0; + private _threasHold: number = 0; + + /** + * Background color of the component. Default is white. + */ + @Input() + get backgroundColor() { + if (this._backgroundColor) { + return this._backgroundColor; + } + return '#FAFAFA'; + } + + set backgroundColor(value: string) { + this._backgroundColor = value; + } + + private _positionOne: number; + /** + * Keep these two properties + * Percentage of the total width for the first portion of the bar. + * Bar one is rendered above bar two, so bar two's position should always + * be greater than bar one if you want bar two to be visible. + */ + @Input() + get positionOne(): number { + return this._positionOne; + } + + set positionOne(value: number) { + this._positionOne = value; + this.setBars(); + } + + private _positionTwo: number; + /** + * Percentage of the total width for the second portion of the bar + */ + @Input() + get positionTwo(): number { + return this._positionTwo; + } + + set positionTwo(value: number) { + this._positionTwo = this._positionOne + value; + this.setBars(); + } + + private _animate: boolean; + /** + * Whether to animate transitions in the bars + */ + @Input() + get animate(): boolean { + return this._animate; + } + + set animate(value: boolean) { + if (typeof value !== 'undefined') { + this._animate = value; + } + this.setAnimate(); + } + + //Define the gauge size + @Input() + get size(): string { + return this._size; + } + + set size(sz: string) { + if (typeof sz !== 'undefined') { + if (sz === 'small' || sz === 'medium' || sz === 'large') { + this._size = sz; + return; + } + } + + this._size = "small"; + } + + get sizeClass(): string { + return "esxc-gauge-" + this._size; + } + + @Input() + get title(): string { + return this._title; + } + + set title(t: string) { + if (typeof t !== 'undefined') { + this._title = t; + } + } + + @Input() + get used(): number { + return this._used; + } + + set used(u: number) { + this._used = u; + this.determineColors(); + } + + @Input() + get threasHold(): number { + return this._threasHold; + } + + set threasHold(th: number) { + this._threasHold = th; + this.determineColors(); + } + + ngAfterViewInit() { + this.determineColors(); + } + + @ViewChild('barOne') private barOne: ElementRef; + @ViewChild('barTwo') private barTwo: ElementRef; + + private determineColors() { + console.info(this._used, this._threasHold); + let percent: number = 0; + if (this._threasHold !== 0) { + percent = (this._used / this._threasHold) * 100; + } + + while (percent > 100) { + percent = percent - 100; + } + + if (percent <= 70) { + this._colorOne = RESOURCE_COLOR_GREEN_NORMAL; + } else if (percent > 70 && percent <= 90) { + this._colorOne = RESOURCE_COLOR_ORANGE_NORMAL; + } else if (percent > 90 && percent <= 100) { + this._colorOne = RESOURCE_COLOR_RED_NORMAL; + } else { + this._colorOne = RESOURCE_COLOR_GREY600; + } + + this._positionOne = percent; + this.setBars(); + this.setColors(); + this.setAnimate(); + } + + private setBars() { + if (!this.barOne || !this.barTwo) { + return; + } + + let barOne = $(this.barOne.nativeElement); + let barTwo = $(this.barTwo.nativeElement); + + if (!barOne || !barTwo) { + return; + } + + let posOne, posTwo; + + if (isNaN(this.positionOne)) { + posOne = posTwo = 0; + } else { + posOne = (this.positionOne / 100) * 180; + posTwo = (this.positionTwo / 100) * 180; + } + + barOne.css({ + '-webkit-transform': 'rotate(' + posOne + 'deg)', + '-moz-transform': 'rotate(' + posOne + 'deg)', + '-ms-transform': 'rotate(' + posOne + 'deg)', + '-o-transform': 'rotate(' + posOne + 'deg)', + 'transform': 'rotate(' + posOne + 'deg)' + }); + + barTwo.css({ + '-webkit-transform': 'rotate(' + posTwo + 'deg)', + '-moz-transform': 'rotate(' + posTwo + 'deg)', + '-ms-transform': 'rotate(' + posTwo + 'deg)', + '-o-transform': 'rotate(' + posTwo + 'deg)', + 'transform': 'rotate(' + posTwo + 'deg)' + }); + } + + private setColors() { + if (!this.barOne || !this.barTwo) { + return; + } + + let barOne = $(this.barOne.nativeElement); + let barTwo = $(this.barTwo.nativeElement); + + if (!barOne || !barTwo) { + return; + } + + barOne.css({ + 'background-color': this._colorOne + }); + + barTwo.css({ + 'background-color': this._colorTwo + }); + } + + private setAnimate() { + if (!this.barOne || !this.barTwo) { + return; + } + + let barOne = $(this.barOne.nativeElement); + let barTwo = $(this.barTwo.nativeElement); + + if (!barOne || !barTwo) { + return; + } + + let transition = 'transform 1s ease'; + let prefixes = ['webkit', 'moz', 'ms', 'o']; + let css = { + 'transition': transition + }; + + if (!this._animate) { + transition = 'none'; + }; + + for (let prefix of prefixes) { + css['-' + prefix + '-transition'] = transition; + } + + barOne.css(css); + barTwo.css(css); + } + +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/shared.module.ts b/src/ui_ng/src/app/shared/shared.module.ts index 662bb3d57..b3806a7cc 100644 --- a/src/ui_ng/src/app/shared/shared.module.ts +++ b/src/ui_ng/src/app/shared/shared.module.ts @@ -39,6 +39,7 @@ import { ListRepositoryROComponent } from './list-repository-ro/list-repository- import { MessageHandlerService } from './message-handler/message-handler.service'; import { EmailValidatorDirective } from './email.directive'; +import { GaugeComponent } from './gauge/gauge.component'; @NgModule({ imports: [ @@ -62,7 +63,8 @@ import { EmailValidatorDirective } from './email.directive'; StatisticsPanelComponent, ListProjectROComponent, ListRepositoryROComponent, - EmailValidatorDirective + EmailValidatorDirective, + GaugeComponent ], exports: [ CoreModule, @@ -82,7 +84,8 @@ import { EmailValidatorDirective } from './email.directive'; StatisticsPanelComponent, ListProjectROComponent, ListRepositoryROComponent, - EmailValidatorDirective + EmailValidatorDirective, + GaugeComponent ], providers: [ SessionService, diff --git a/src/ui_ng/src/app/shared/statictics/statistics-panel.component.html b/src/ui_ng/src/app/shared/statictics/statistics-panel.component.html index 5d9b91aff..ad42bcc70 100644 --- a/src/ui_ng/src/app/shared/statictics/statistics-panel.component.html +++ b/src/ui_ng/src/app/shared/statictics/statistics-panel.component.html @@ -35,10 +35,10 @@ -
-
-
{{freeStorage}}GB | {{totalStorage}}GB
-
[STORAGE]
+
+
+ +
\ No newline at end of file diff --git a/src/ui_ng/src/app/shared/statictics/statistics-panel.component.ts b/src/ui_ng/src/app/shared/statictics/statistics-panel.component.ts index 06165686c..c2cfe37dd 100644 --- a/src/ui_ng/src/app/shared/statictics/statistics-panel.component.ts +++ b/src/ui_ng/src/app/shared/statictics/statistics-panel.component.ts @@ -64,6 +64,10 @@ export class StatisticsPanelComponent implements OnInit { return user && user.has_admin_role > 0; } + public get isValidStorage(): boolean { + return this.volumesInfo.storage.total != 0; + } + private getGBFromBytes(bytes: number): number { return Math.round((bytes / (1024 * 1024 * 1024))); } diff --git a/src/ui_ng/src/i18n/lang/en-us-lang.json b/src/ui_ng/src/i18n/lang/en-us-lang.json index a0cb6f1c4..d217bf03f 100644 --- a/src/ui_ng/src/i18n/lang/en-us-lang.json +++ b/src/ui_ng/src/i18n/lang/en-us-lang.json @@ -327,13 +327,13 @@ "COPY": "Copy" }, "ALERT": { - "FORM_CHANGE_CONFIRMATION": "Some changes are not saved yet, do you want to cancel?" + "FORM_CHANGE_CONFIRMATION": "Some changes are not saved yet. Do you want to cancel?" }, "RESET_PWD": { "TITLE": "Reset Password", "CAPTION": "Enter your email to reset your password", "EMAIL": "Email", - "SUCCESS": "Mail of resetting password is successfully send to your mail box", + "SUCCESS": "Mail with password resetting is successfully sent. You can close this dialog and check your mailbox now.", "CAPTION2": "Enter your new password", "RESET_OK": "Password has been successfully reset. Click OK to login with new password" }, @@ -394,7 +394,7 @@ "TEST_MAIL_FAILED": "Failed to verify mail server with error: {{param}}", "TEST_LDAP_FAILED": "Failed to verify LDAP server with error: {{param}}", "LEAVING_CONFIRMATION_TITLE": "Confirm to leave", - "LEAVING_CONFIRMATION_SUMMARY": "Changes have not been saved yet, do you want to leave currnet page?" + "LEAVING_CONFIRMATION_SUMMARY": "Changes have not been saved yet. Do you want to leave currnet page?" }, "PAGE_NOT_FOUND": { "MAIN_TITLE": "Page not found", @@ -404,7 +404,7 @@ "ABOUT": { "VERSION": "Version", "BUILD": "Build", - "COPYRIGHT": "Copyright 1998-2016 VMware. Inc. All rights reserved. This product is protected by U.S. and international property laws. VMware products are covered by one or more patents listed at", + "COPYRIGHT": "Copyright 1998-2017 VMware. Inc. All rights reserved. This product is protected by U.S. and international property laws. VMware products are covered by one or more patents listed at", "TRADEMARK": "VMware is a registered trademark or trademark of VMware. Inc. in the United States and other jurisdictions. All other marks and names mentioned herein may be trademark of their respective companies.", "END_USER_LICENSE": "End User License Agreement", "OPEN_SOURCE_LICENSE": "Open Source/Third Party License" @@ -420,7 +420,9 @@ "REPO_ITEM": "REPOSITORIES", "INDEX_MY": "MY", "INDEX_PUB": "PUBLIC", - "INDEX_TOTAL": "TOTAL" + "INDEX_TOTAL": "TOTAL", + "STORAGE": "STORAGE", + "LIMIT": "Limit" }, "SEARCH": { "IN_PROGRESS": "Search...", diff --git a/src/ui_ng/src/i18n/lang/zh-cn-lang.json b/src/ui_ng/src/i18n/lang/zh-cn-lang.json index c57149521..332e1532f 100644 --- a/src/ui_ng/src/i18n/lang/zh-cn-lang.json +++ b/src/ui_ng/src/i18n/lang/zh-cn-lang.json @@ -333,7 +333,7 @@ "TITLE": "重置密码", "CAPTION": "输入用来重置密码的邮箱", "EMAIL": "邮箱", - "SUCCESS": "重置密码邮件已成功发送", + "SUCCESS": "重置密码邮件已成功发送. 请关闭对话框并检查邮箱", "CAPTION2": "请输入您的新密码", "RESET_OK": "密码重置成功,点击确定按钮前往登录页登录" }, @@ -404,7 +404,7 @@ "ABOUT": { "VERSION": "版本", "BUILD": "构建", - "COPYRIGHT": "Copyright 1998-2016 VMware. Inc. All rights reserved. This product is protected by U.S. and international property laws. VMware products are covered by one or more patents listed at", + "COPYRIGHT": "Copyright 1998-2017 VMware. Inc. All rights reserved. This product is protected by U.S. and international property laws. VMware products are covered by one or more patents listed at", "TRADEMARK": "Vmware is a registered trademark or trademark of VMware. Inc. in the United States and other jurisdictions. All other marks and names mentioned herein may be trademark of their respective companies.", "END_USER_LICENSE": "终端用户许可协议", "OPEN_SOURCE_LICENSE": "开源/第三方许可协议" @@ -420,7 +420,9 @@ "REPO_ITEM": "镜像库", "INDEX_MY": "私有", "INDEX_PUB": "公开", - "INDEX_TOTAL": "总计" + "INDEX_TOTAL": "总计", + "STORAGE": "存储", + "LIMIT": "容量" }, "SEARCH": { "IN_PROGRESS": "搜索中...",