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 @@
+
+
+
+ {{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 867367095..d2badf012 100644
--- a/src/ui_ng/src/i18n/lang/en-us-lang.json
+++ b/src/ui_ng/src/i18n/lang/en-us-lang.json
@@ -413,7 +413,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 f22f80d9c..507e28e79 100644
--- a/src/ui_ng/src/i18n/lang/zh-cn-lang.json
+++ b/src/ui_ng/src/i18n/lang/zh-cn-lang.json
@@ -413,7 +413,9 @@
"REPO_ITEM": "镜像库",
"INDEX_MY": "私有",
"INDEX_PUB": "公开",
- "INDEX_TOTAL": "总计"
+ "INDEX_TOTAL": "总计",
+ "STORAGE": "存储",
+ "LIMIT": "容量"
},
"SEARCH": {
"IN_PROGRESS": "搜索中...",