mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-05 18:20:37 +01:00
commit
7d42b73f83
@ -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",
|
||||
|
@ -7,10 +7,10 @@
|
||||
<section class="form-block">
|
||||
<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"
|
||||
<label for="reset_pwd_email" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]="!validationState">
|
||||
<input [disabled]="isSuccess" name="reset_pwd_email" type="text" #eamilInput="ngModel" [(ngModel)]="email"
|
||||
required
|
||||
pattern='^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$'
|
||||
email
|
||||
id="reset_pwd_email"
|
||||
size="40"
|
||||
(input)="handleValidation(true)"
|
||||
@ -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()">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" [disabled]="!isValid || showProgress" (click)="send()">{{'BUTTON.SEND' | translate}}</button>
|
||||
<button type="button" class="btn btn-outline" (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>
|
@ -15,7 +15,7 @@ export class ForgotPasswordComponent {
|
||||
private onGoing: boolean = false;
|
||||
private email: string = "";
|
||||
private validationState: boolean = true;
|
||||
private forceValid: boolean = true;
|
||||
private isSuccess: boolean = false;
|
||||
|
||||
@ViewChild("forgotPasswordFrom") forgotPwdForm: NgForm;
|
||||
@ViewChild(InlineAlertComponent)
|
||||
@ -28,13 +28,21 @@ export class ForgotPasswordComponent {
|
||||
}
|
||||
|
||||
public get isValid(): boolean {
|
||||
return this.forgotPwdForm && this.forgotPwdForm.valid && this.forceValid;
|
||||
return this.forgotPwdForm && this.forgotPwdForm.valid ;
|
||||
}
|
||||
|
||||
public get btnCancelCaption(): string {
|
||||
if(this.isSuccess){
|
||||
return "BUTTON.CLOSE";
|
||||
}
|
||||
|
||||
return "BUTTON.CANCEL";
|
||||
}
|
||||
|
||||
public open(): void {
|
||||
//Clear state data
|
||||
this.validationState = true;
|
||||
this.forceValid = true;
|
||||
this.isSuccess = false;
|
||||
this.onGoing = false;
|
||||
this.email = "";
|
||||
this.forgotPwdForm.resetForm();
|
||||
@ -61,7 +69,7 @@ export class ForgotPasswordComponent {
|
||||
this.pwdService.sendResetPasswordMail(this.email)
|
||||
.then(response => {
|
||||
this.onGoing = false;
|
||||
this.forceValid = false;//diable the send button
|
||||
this.isSuccess = true;
|
||||
this.inlineAlert.showInlineSuccess({
|
||||
message: "RESET_PWD.SUCCESS"
|
||||
});
|
||||
|
@ -7,7 +7,7 @@
|
||||
<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'>
|
||||
<input type="password" id="newPassword" placeholder='{{"PLACEHOLDER.NEW_PWD" | translate}}'
|
||||
<input [disabled]="resetOk" type="password" id="newPassword" placeholder='{{"PLACEHOLDER.NEW_PWD" | translate}}'
|
||||
required
|
||||
pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{8,20}$"
|
||||
name="newPassword"
|
||||
@ -24,7 +24,7 @@
|
||||
<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'>
|
||||
<input type="password" id="reNewPassword" placeholder='{{"PLACEHOLDER.CONFIRM_PWD" | translate}}'
|
||||
<input [disabled]="resetOk" type="password" id="reNewPassword" placeholder='{{"PLACEHOLDER.CONFIRM_PWD" | translate}}'
|
||||
required
|
||||
pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{8,20}$"
|
||||
name="reNewPassword"
|
||||
@ -45,7 +45,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()">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" [disabled]="!isValid || showProgress" (click)="send()">{{'BUTTON.OK' | translate}}</button>
|
||||
<button type="button" class="btn btn-outline" (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>
|
@ -5,6 +5,7 @@ import { NgForm } from '@angular/forms';
|
||||
import { PasswordSettingService } from './password-setting.service';
|
||||
import { InlineAlertComponent } from '../../shared/inline-alert/inline-alert.component';
|
||||
import { MessageHandlerService } from '../../shared/message-handler/message-handler.service';
|
||||
import { CommonRoutes } from '../../shared/shared.const';
|
||||
|
||||
@Component({
|
||||
selector: 'reset-password',
|
||||
@ -45,6 +46,14 @@ export class ResetPasswordComponent implements OnInit {
|
||||
return this.resetPwdForm && this.resetPwdForm.valid && this.samePassword();
|
||||
}
|
||||
|
||||
public get btnCancelCaption(): string {
|
||||
if (!this.resetOk) {
|
||||
return 'BUTTON.CANCEL';
|
||||
} else {
|
||||
return 'BUTTON.CLOSE';
|
||||
}
|
||||
}
|
||||
|
||||
public getValidationState(key: string): boolean {
|
||||
return this.validationState &&
|
||||
this.validationState[key] &&
|
||||
@ -65,16 +74,14 @@ export class ResetPasswordComponent implements OnInit {
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
//If already reset password ok, navigator to sign-in
|
||||
if (this.resetOk) {
|
||||
this.router.navigateByUrl(CommonRoutes.EMBEDDED_SIGN_IN);
|
||||
}
|
||||
this.opened = false;
|
||||
}
|
||||
|
||||
public send(): void {
|
||||
//If already reset password ok, navigator to sign-in
|
||||
if (this.resetOk) {
|
||||
this.router.navigate(['sign-in']);
|
||||
return;
|
||||
}
|
||||
|
||||
//Double confirm to avoid improper situations
|
||||
if (!this.password) {
|
||||
return;
|
||||
|
@ -24,7 +24,7 @@
|
||||
<div class="checkbox">
|
||||
<input type="checkbox" id="rememberme" #rememberMeBox (click)="clickRememberMe($event)" [checked]="rememberMe">
|
||||
<label for="rememberme">{{ 'SIGN_IN.REMEMBER' | translate }}</label>
|
||||
<a href="javascript:void(0)" class="forgot-password-link" (click)="forgotPassword()">{{'SIGN_IN.FORGOT_PWD' | translate}}</a>
|
||||
<a *ngIf="showForgetPwd" href="javascript:void(0)" class="forgot-password-link" (click)="forgotPassword()">{{'SIGN_IN.FORGOT_PWD' | translate}}</a>
|
||||
</div>
|
||||
<div [class.visibility-hidden]="!isError" class="error active">
|
||||
{{ 'SIGN_IN.INVALID_MSG' | translate }}
|
||||
|
@ -109,6 +109,10 @@ export class SignInComponent implements AfterViewChecked, OnInit {
|
||||
&& this.appConfig.self_registration;
|
||||
}
|
||||
|
||||
public get showForgetPwd(): boolean {
|
||||
return this.appConfig.auth_mode != 'ldap_auth';
|
||||
}
|
||||
|
||||
private clickRememberMe($event): void {
|
||||
if ($event && $event.target) {
|
||||
this.rememberMe = $event.target.checked;
|
||||
|
@ -9,7 +9,7 @@
|
||||
</select>
|
||||
</div>
|
||||
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right">
|
||||
<clr-icon shape="info-circle" class="is-info" size="24"></clr-icon>
|
||||
<clr-icon shape="info-circle" size="24" style="color: grey;"></clr-icon>
|
||||
<span class="tooltip-content">{{'CONFIG.TOOLTIP.AUTH_MODE' | translate}}</span>
|
||||
</a>
|
||||
</div>
|
||||
@ -40,7 +40,7 @@
|
||||
</span>
|
||||
</label>
|
||||
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right">
|
||||
<clr-icon shape="info-circle" class="is-info" size="24"></clr-icon>
|
||||
<clr-icon shape="info-circle" style="color: grey;" size="24"></clr-icon>
|
||||
<span class="tooltip-content">{{'CONFIG.TOOLTIP.LDAP_SEARCH_DN' | translate}}</span>
|
||||
</a>
|
||||
</div>
|
||||
@ -68,7 +68,7 @@
|
||||
</span>
|
||||
</label>
|
||||
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right">
|
||||
<clr-icon shape="info-circle" class="is-info" size="24"></clr-icon>
|
||||
<clr-icon shape="info-circle" style="color: grey;" size="24"></clr-icon>
|
||||
<span class="tooltip-content">{{'CONFIG.TOOLTIP.LDAP_BASE_DN' | translate}}</span>
|
||||
</a>
|
||||
</div>
|
||||
@ -95,7 +95,7 @@
|
||||
</span>
|
||||
</label>
|
||||
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right">
|
||||
<clr-icon shape="info-circle" class="is-info" size="24"></clr-icon>
|
||||
<clr-icon shape="info-circle" style="color: grey;" size="24"></clr-icon>
|
||||
<span class="tooltip-content">{{'CONFIG.TOOLTIP.LDAP_UID' | translate}}</span>
|
||||
</a>
|
||||
</div>
|
||||
@ -109,7 +109,7 @@
|
||||
</select>
|
||||
</div>
|
||||
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right">
|
||||
<clr-icon shape="info-circle" class="is-info" size="24"></clr-icon>
|
||||
<clr-icon shape="info-circle" style="color: grey;" size="24"></clr-icon>
|
||||
<span class="tooltip-content">{{'CONFIG.TOOLTIP.LDAP_SCOPE' | translate}}</span>
|
||||
</a>
|
||||
</div>
|
||||
@ -124,7 +124,7 @@
|
||||
</select>
|
||||
</div>
|
||||
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right">
|
||||
<clr-icon shape="info-circle" class="is-info" size="24"></clr-icon>
|
||||
<clr-icon shape="info-circle" style="color: grey;" size="24"></clr-icon>
|
||||
<span class="tooltip-content">{{'CONFIG.TOOLTIP.PRO_CREATION_RESTRICTION' | translate}}</span>
|
||||
</a>
|
||||
</div>
|
||||
@ -132,7 +132,7 @@
|
||||
<label for="selfReg">{{'CONFIG.SELF_REGISTRATION' | translate}}</label>
|
||||
<clr-checkbox name="selfReg" id="selfReg" [(ngModel)]="currentConfig.self_registration.value" [disabled]="disabled(currentConfig.self_registration)">
|
||||
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right" style="top:-8px;">
|
||||
<clr-icon shape="info-circle" class="is-info" size="24"></clr-icon>
|
||||
<clr-icon shape="info-circle" style="color: grey;" size="24"></clr-icon>
|
||||
<span class="tooltip-content">{{'CONFIG.TOOLTIP.SELF_REGISTRATION' | translate}}</span>
|
||||
</a>
|
||||
</clr-checkbox>
|
||||
|
@ -26,7 +26,7 @@
|
||||
<label for="verifyRemoteCert">{{'CONFIG.VERIFY_REMOTE_CERT' | translate }}</label>
|
||||
<clr-checkbox name="verifyRemoteCert" id="verifyRemoteCert" [(ngModel)]="allConfig.verify_remote_cert.value" [disabled]="disabled(allConfig.verify_remote_cert)">
|
||||
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-lg tooltip-top-right" style="top:-8px;">
|
||||
<clr-icon shape="info-circle" class="is-info" size="24"></clr-icon>
|
||||
<clr-icon shape="info-circle" style="color: grey;" size="24"></clr-icon>
|
||||
<span class="tooltip-content">{{'CONFIG.TOOLTIP.VERIFY_REMOTE_CERT' | translate }}</span>
|
||||
</a>
|
||||
</clr-checkbox>
|
||||
@ -53,7 +53,7 @@
|
||||
</span>
|
||||
</label>
|
||||
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right">
|
||||
<clr-icon shape="info-circle" class="is-info" size="24"></clr-icon>
|
||||
<clr-icon shape="info-circle" style="color: grey;" size="24"></clr-icon>
|
||||
<span class="tooltip-content">{{'CONFIG.TOOLTIP.TOKEN_EXPIRATION' | translate}}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
@ -63,7 +63,7 @@
|
||||
<label for="selfReg">{{'CONFIG.MAIL_SSL' | translate}}</label>
|
||||
<clr-checkbox name="emailSSL" id="emailSSL" [(ngModel)]="currentConfig.email_ssl.value" [disabled]="disabled(currentConfig.email_ssl)">
|
||||
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right" style="top:-8px;">
|
||||
<clr-icon shape="info-circle" class="is-info" size="24"></clr-icon>
|
||||
<clr-icon shape="info-circle" style="color: grey;" size="24"></clr-icon>
|
||||
<span class="tooltip-content">{{'CONFIG.SSL_TOOLTIP' | translate}}</span>
|
||||
</a>
|
||||
</clr-checkbox>
|
||||
|
284
src/ui_ng/src/app/shared/gauge/gauge.component.css
Normal file
284
src/ui_ng/src/app/shared/gauge/gauge.component.css
Normal file
@ -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;
|
||||
}
|
18
src/ui_ng/src/app/shared/gauge/gauge.component.html
Normal file
18
src/ui_ng/src/app/shared/gauge/gauge.component.html
Normal file
@ -0,0 +1,18 @@
|
||||
<div class="{{sizeClass}}">
|
||||
<div class="esxc-gauge-container esxc-gauge">
|
||||
<div class="esxc-gauge-circle-bg"></div>
|
||||
<div #barOne class="esxc-gauge-circle-fill esxc-gauge-bar-one"></div>
|
||||
<div #barTwo class="esxc-gauge-circle-fill esxc-gauge-bar-two"></div>
|
||||
<div class="esxc-gauge-circle-inner" [ngStyle]="{'background-color': backgroundColor}">
|
||||
<div class="esxc-gauge-circle-caption">
|
||||
<span class="esxc-value">{{used}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="esxc-limit">
|
||||
<span class="esxc-value">{{threasHold}}</span>
|
||||
<span class="esxc-unit">GB</span>
|
||||
<span class="esxc-label">{{'STATISTICS.LIMIT' | translate}}</span>
|
||||
</div>
|
||||
<div class="esxc-title">{{title | translate}}</div>
|
||||
</div>
|
272
src/ui_ng/src/app/shared/gauge/gauge.component.ts
Normal file
272
src/ui_ng/src/app/shared/gauge/gauge.component.ts
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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,
|
||||
|
@ -35,10 +35,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="statistic-item-divider" [hidden]="!isValidSession"></div>
|
||||
<div class="statistic-block" [hidden]="!isValidSession">
|
||||
<div>{{freeStorage}}GB | {{totalStorage}}GB</div>
|
||||
<div>[STORAGE]</div>
|
||||
<div class="statistic-item-divider" [hidden]="!isValidSession || !isValidStorage"></div>
|
||||
<div class="statistic-block" [hidden]="!isValidSession || !isValidStorage">
|
||||
<esxc-gauge [used]="freeStorage" [threasHold]="totalStorage" [size]="small" [title]='"STATISTICS.STORAGE"' [animate]="true">
|
||||
</esxc-gauge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -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)));
|
||||
}
|
||||
|
@ -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...",
|
||||
|
@ -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": "搜索中...",
|
||||
|
Loading…
Reference in New Issue
Block a user