mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-31 21:18:21 +01:00
Fix legacy issues that html and css file are written on ts file.
Currently, our html and css files are written as string on .ts file. This pr is to solve the legacy issue.
This commit is contained in:
parent
b4b148e923
commit
282a63f57f
@ -81,7 +81,7 @@ script:
|
||||
- sudo mkdir -p /harbor
|
||||
- sudo mv ./VERSION /harbor/UIVERSION
|
||||
- sudo service postgresql stop
|
||||
- sudo make run_clarity_ut CLARITYIMAGE=vmware/harbor-clarity-ui-builder:1.4.0
|
||||
- sudo make run_clarity_ut CLARITYIMAGE=vmware/harbor-clarity-ui-builder:1.4.1
|
||||
- cat ./src/ui_ng/npm-ut-test-results
|
||||
- sudo ./tests/testprepare.sh
|
||||
- sudo make -f make/photon/Makefile _build_postgresql _build_db _build_registry -e VERSIONTAG=dev -e CLAIRDBVERSION=dev -e REGISTRYVERSION=v2.6.2
|
||||
@ -107,7 +107,7 @@ script:
|
||||
- sudo rm -rf /data/config/*
|
||||
- sudo rm -rf /data/database/*
|
||||
- ls /data/cert
|
||||
- sudo make install GOBUILDIMAGE=golang:1.9.2 COMPILETAG=compile_golangimage CLARITYIMAGE=vmware/harbor-clarity-ui-builder:1.4.0 NOTARYFLAG=true CLAIRFLAG=true
|
||||
- sudo make install GOBUILDIMAGE=golang:1.9.2 COMPILETAG=compile_golangimage CLARITYIMAGE=vmware/harbor-clarity-ui-builder:1.4.1 NOTARYFLAG=true CLAIRFLAG=true
|
||||
- sleep 10
|
||||
- docker ps
|
||||
- ./tests/validatecontainers.sh
|
||||
|
@ -34,7 +34,7 @@ sed -i 's/* as//g' src/app/shared/gauge/gauge.component.js
|
||||
cp ./dist/build.min.js ../ui/static/
|
||||
|
||||
cp -r ./src/i18n/ ../ui/static/
|
||||
cp ./src/styles.css ../ui/static/
|
||||
cp ./src/styles.scss ../ui/static/
|
||||
cp -r ./src/images/ ../ui/static/
|
||||
cp ./src/setting.json ../ui/static/
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
"styles": [
|
||||
"../node_modules/clarity-icons/clarity-icons.min.css",
|
||||
"../node_modules/clarity-ui/clarity-ui.min.css",
|
||||
"styles.css"
|
||||
"styles.scss"
|
||||
],
|
||||
"scripts": [
|
||||
"../node_modules/core-js/client/shim.min.js",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "harbor-ui",
|
||||
"version": "0.7.18-dev.1",
|
||||
"version": "0.7.18-dev.6",
|
||||
"description": "Harbor shared UI components based on Clarity and Angular4",
|
||||
"author": "VMware",
|
||||
"module": "index.js",
|
||||
|
@ -1,4 +1,3 @@
|
||||
export const REGISTRY_CONFIG_HTML: string = `
|
||||
<div>
|
||||
<system-settings #systemSettings [(systemSettings)]="config" [showSubTitle]="true" [hasAdminRole]="hasAdminRole" [hasCAFile]="hasCAFile" [withAdmiral]="withAdmiral"></system-settings>
|
||||
<vulnerability-config *ngIf="withClair" #vulnerabilityConfig [(vulnerabilityConfig)]="config" [showSubTitle]="true"></vulnerability-config>
|
||||
@ -7,5 +6,4 @@ export const REGISTRY_CONFIG_HTML: string = `
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()" [disabled]="shouldDisable">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
</div>
|
||||
<confirmation-dialog #cfgConfirmationDialog (confirmAction)="confirmCancel($event)"></confirmation-dialog>
|
||||
</div>
|
||||
`;
|
||||
</div>
|
@ -1,9 +1,7 @@
|
||||
export const REGISTRY_CONFIG_STYLES: string = `
|
||||
.info-tips-icon {
|
||||
color: grey;
|
||||
}
|
||||
|
||||
.info-tips-icon:hover {
|
||||
color: #007CBB;
|
||||
}
|
||||
`;
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
import { Component, OnInit, EventEmitter, Output, ViewChild, Input } from '@angular/core';
|
||||
|
||||
import { Configuration, ComplexValueItem } from './config';
|
||||
import { REGISTRY_CONFIG_HTML } from './registry-config.component.html';
|
||||
import { ConfigurationService, SystemInfoService, SystemInfo, ClairDBStatus } from '../service/index';
|
||||
import {
|
||||
toPromise,
|
||||
@ -24,7 +23,7 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-registry-config',
|
||||
template: REGISTRY_CONFIG_HTML
|
||||
templateUrl: './registry-config.component.html'
|
||||
})
|
||||
export class RegistryConfigComponent implements OnInit {
|
||||
config: Configuration = new Configuration();
|
||||
|
@ -1,4 +1,3 @@
|
||||
export const REPLICATION_CONFIG_HTML: string = `
|
||||
<form #replicationConfigFrom="ngForm" class="compact">
|
||||
<section class="form-block" style="margin-top:0px;margin-bottom:0px;">
|
||||
<label style="font-size:14px;font-weight:600;" *ngIf="showSubTitle">{{'CONFIG.REPLICATION' | translate}}</label>
|
||||
@ -13,4 +12,3 @@ export const REPLICATION_CONFIG_HTML: string = `
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
`;
|
@ -1,14 +1,12 @@
|
||||
import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core';
|
||||
import { NgForm } from '@angular/forms';
|
||||
|
||||
import { REPLICATION_CONFIG_HTML } from './replication-config.component.html';
|
||||
import { Configuration } from '../config';
|
||||
import { REGISTRY_CONFIG_STYLES } from '../registry-config.component.css';
|
||||
|
||||
@Component({
|
||||
selector: 'replication-config',
|
||||
template: REPLICATION_CONFIG_HTML,
|
||||
styles: [REGISTRY_CONFIG_STYLES]
|
||||
templateUrl: './replication-config.component.html',
|
||||
styles: ['./replication-config.component.scss', '../registry-config.component.scss']
|
||||
})
|
||||
export class ReplicationConfigComponent {
|
||||
config: Configuration;
|
||||
|
@ -1,4 +1,3 @@
|
||||
export const SYSTEM_SETTINGS_HTML: string = `
|
||||
<form #systemConfigFrom="ngForm" class="compact">
|
||||
<section class="form-block" style="margin-top:0px;margin-bottom:0px;">
|
||||
<label style="font-size:14px;font-weight:600;" *ngIf="showSubTitle">{{'CONFIG.SYSTEM' | translate}}</label>
|
||||
@ -37,5 +36,4 @@ export const SYSTEM_SETTINGS_HTML: string = `
|
||||
</clr-checkbox>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
`;
|
||||
</form>
|
@ -1,15 +1,13 @@
|
||||
import { Component, Input, Output, EventEmitter, ViewChild, Inject } from '@angular/core';
|
||||
import { NgForm } from '@angular/forms';
|
||||
|
||||
import { SYSTEM_SETTINGS_HTML } from './system-settings.component.html';
|
||||
import { Configuration } from '../config';
|
||||
import { REGISTRY_CONFIG_STYLES } from '../registry-config.component.css';
|
||||
import { SERVICE_CONFIG, IServiceConfig } from '../../service.config';
|
||||
|
||||
@Component({
|
||||
selector: 'system-settings',
|
||||
template: SYSTEM_SETTINGS_HTML,
|
||||
styles: [REGISTRY_CONFIG_STYLES]
|
||||
templateUrl: './system-settings.component.html',
|
||||
styleUrls: ['./system-settings.component.scss', '../registry-config.component.scss']
|
||||
})
|
||||
export class SystemSettingsComponent {
|
||||
config: Configuration;
|
||||
|
@ -1,4 +1,3 @@
|
||||
export const VULNERABILITY_CONFIG_HTML: string = `
|
||||
<form #systemConfigFrom="ngForm" class="compact">
|
||||
<section class="form-block" style="margin-top:0px;margin-bottom:0px;">
|
||||
<label class="section-title" *ngIf="showSubTitle">{{ 'CONFIG.SCANNING.TITLE' | translate }}</label>
|
||||
@ -43,39 +42,4 @@ export const VULNERABILITY_CONFIG_HTML: string = `
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
`;
|
||||
|
||||
export const VULNERABILITY_CONFIG_STYLES: string = `
|
||||
.form-group-override {
|
||||
padding-left: 0px !important;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 14px !important;
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
|
||||
.btn-font {
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
.namespace {
|
||||
margin-left: 24px;
|
||||
}
|
||||
|
||||
.clr-dropdown-override {
|
||||
margin-top: -8px;
|
||||
}
|
||||
.btn-scan-right{
|
||||
margin-left: 10px;
|
||||
}
|
||||
.btn-scan-right button{
|
||||
width: 160px;
|
||||
margin-bottom: 0px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
.btn-scan-right span{
|
||||
margin-top: 4px;
|
||||
}
|
||||
`;
|
||||
</form>
|
@ -0,0 +1,31 @@
|
||||
.form-group-override {
|
||||
padding-left: 0px !important;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 14px !important;
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
|
||||
.btn-font {
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
.namespace {
|
||||
margin-left: 24px;
|
||||
}
|
||||
|
||||
.clr-dropdown-override {
|
||||
margin-top: -8px;
|
||||
}
|
||||
.btn-scan-right{
|
||||
margin-left: 10px;
|
||||
}
|
||||
.btn-scan-right button{
|
||||
width: 160px;
|
||||
margin-bottom: 0px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
.btn-scan-right span{
|
||||
margin-top: 4px;
|
||||
}
|
@ -2,7 +2,6 @@ import { Component, Input, Output, EventEmitter, ViewChild, OnInit } from '@angu
|
||||
import { NgForm } from '@angular/forms';
|
||||
|
||||
import { Configuration } from '../config';
|
||||
import { VULNERABILITY_CONFIG_HTML, VULNERABILITY_CONFIG_STYLES } from './vulnerability-config.component.template';
|
||||
import {
|
||||
ScanningResultService,
|
||||
SystemInfo,
|
||||
@ -13,15 +12,13 @@ import { toPromise } from '../../utils';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { ClairDBStatus, ClairDetail } from '../../service/interface';
|
||||
|
||||
import { REGISTRY_CONFIG_STYLES } from '../registry-config.component.css';
|
||||
|
||||
const ONE_HOUR_SECONDS: number = 3600;
|
||||
const ONE_DAY_SECONDS: number = 24 * ONE_HOUR_SECONDS;
|
||||
|
||||
@Component({
|
||||
selector: 'vulnerability-config',
|
||||
template: VULNERABILITY_CONFIG_HTML,
|
||||
styles: [VULNERABILITY_CONFIG_STYLES, REGISTRY_CONFIG_STYLES]
|
||||
templateUrl: './vulnerability-config.component.html',
|
||||
styles: ['./vulnerability-config.component.scss', '../registry-config.component.scss']
|
||||
})
|
||||
export class VulnerabilityConfigComponent implements OnInit {
|
||||
_localTime: Date = new Date();
|
||||
|
@ -1,30 +0,0 @@
|
||||
export const CONFIRMATION_DIALOG_STYLE: string = `
|
||||
.confirmation-icon-inline {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.confirmation-title {
|
||||
line-height: 24px;
|
||||
color: #000000;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.confirmation-content {
|
||||
font-size: 14px;
|
||||
color: #565656;
|
||||
line-height: 24px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 80%;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.batchInfoUl{
|
||||
padding: 20px; list-style-type: none;
|
||||
}
|
||||
.batchInfoUl li {line-height: 24px;border-bottom: 1px solid #e8e8e8;}
|
||||
.batchInfoUl li span:first-child {padding-right: 20px; width: 240px; display: inline-block; color:#666;
|
||||
text-overflow: ellipsis; overflow: hidden; vertical-align: middle;}
|
||||
.batchInfoUl li span:last-child {width: 220px; display: inline-block; color:#666;}
|
||||
.batchInfoUl li span i {display: inline-block; line-height: 1.2em; font-size: 0.8em; color: #999;}
|
||||
.batchInfoUl li span a{cursor: pointer; text-decoration: underline;}
|
||||
`;
|
@ -0,0 +1,45 @@
|
||||
<clr-modal [(clrModalOpen)]="opened" [clrModalClosable]="false" [clrModalStaticBackdrop]="true">
|
||||
<h3 class="modal-title" class="confirmation-title" style="margin-top: 0px;">{{dialogTitle}}</h3>
|
||||
<div class="modal-body">
|
||||
<div class="confirmation-icon-inline">
|
||||
<clr-icon shape="warning" class="is-warning" size="64"></clr-icon>
|
||||
</div>
|
||||
<div class="confirmation-content">{{dialogContent}}</div>
|
||||
<div>
|
||||
<ul class="batchInfoUl">
|
||||
<li *ngFor="let info of batchInfors">
|
||||
<span><i class="spinner spinner-inline spinner-pos" [hidden]='!info.loading'></i> {{info.name}}</span>
|
||||
<span *ngIf="!info.errorInfo.length" [style.color]="colorChange(info)">{{info.status}}</span>
|
||||
<span *ngIf="info.errorInfo.length" [style.color]="colorChange(info)">
|
||||
<a (click)="toggleErrorTitle(errorInfo)">{{info.status}}</a>
|
||||
<br>
|
||||
<i #errorInfo style="display: none;">{{info.errorInfo}}</i>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer" [ngSwitch]="buttons">
|
||||
<ng-template [ngSwitchCase]="0">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="confirm()">{{'BUTTON.CONFIRM' | translate}}</button>
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="1">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()">{{'BUTTON.NO' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="confirm()">{{ 'BUTTON.YES' | translate}}</button>
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="2">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()" [hidden]="isDelete">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-danger" (click)="operate()" [hidden]="isDelete">{{'BUTTON.DELETE' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="cancel()" [disabled]="!batchOverStatus" [hidden]="!isDelete">{{'BUTTON.CLOSE' | translate}}</button>
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="3">
|
||||
<button type="button" class="btn btn-primary" (click)="cancel()">{{'BUTTON.CLOSE' | translate}}</button>
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="4">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()" [hidden]="isDelete">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="operate()" [hidden]="isDelete">{{'BUTTON.REPLICATE' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="cancel()" [disabled]="!batchOverStatus" [hidden]="!isDelete">{{'BUTTON.CLOSE' | translate}}</button>
|
||||
</ng-template>
|
||||
</div>
|
||||
</clr-modal>
|
@ -1,46 +0,0 @@
|
||||
export const CONFIRMATION_DIALOG_TEMPLATE: string = `
|
||||
<clr-modal [(clrModalOpen)]="opened" [clrModalClosable]="false" [clrModalStaticBackdrop]="true">
|
||||
<h3 class="modal-title" class="confirmation-title" style="margin-top: 0px;">{{dialogTitle}}</h3>
|
||||
<div class="modal-body">
|
||||
<div class="confirmation-icon-inline">
|
||||
<clr-icon shape="warning" class="is-warning" size="64"></clr-icon>
|
||||
</div>
|
||||
<div class="confirmation-content">{{dialogContent}}</div>
|
||||
<div>
|
||||
<ul class="batchInfoUl">
|
||||
<li *ngFor="let info of batchInfors">
|
||||
<span> <i class="spinner spinner-inline spinner-pos" [hidden]='!info.loading'></i> {{info.name}}</span>
|
||||
<span *ngIf="!info.errorInfo.length" [style.color]="colorChange(info)">{{info.status}}</span>
|
||||
<span *ngIf="info.errorInfo.length" [style.color]="colorChange(info)">
|
||||
<a (click)="toggleErrorTitle(errorInfo)" >{{info.status}}</a><br>
|
||||
<i #errorInfo style="display: none;">{{info.errorInfo}}</i>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer" [ngSwitch]="buttons">
|
||||
<ng-template [ngSwitchCase]="0">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="confirm()">{{'BUTTON.CONFIRM' | translate}}</button>
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="1">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()">{{'BUTTON.NO' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="confirm()">{{ 'BUTTON.YES' | translate}}</button>
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="2">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()" [hidden]="isDelete">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-danger" (click)="operate()" [hidden]="isDelete">{{'BUTTON.DELETE' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="cancel()" [disabled]="!batchOverStatus" [hidden]="!isDelete">{{'BUTTON.CLOSE' | translate}}</button>
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="3">
|
||||
<button type="button" class="btn btn-primary" (click)="cancel()">{{'BUTTON.CLOSE' | translate}}</button>
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="4">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()" [hidden]="isDelete">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="operate()" [hidden]="isDelete">{{'BUTTON.REPLICATE' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="cancel()" [disabled]="!batchOverStatus" [hidden]="!isDelete">{{'BUTTON.CLOSE' | translate}}</button>
|
||||
</ng-template>
|
||||
</div>
|
||||
</clr-modal>
|
||||
`;
|
@ -0,0 +1,57 @@
|
||||
.confirmation-icon-inline {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.confirmation-title {
|
||||
line-height: 24px;
|
||||
color: #000000;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.confirmation-content {
|
||||
font-size: 14px;
|
||||
color: #565656;
|
||||
line-height: 24px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 80%;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.batchInfoUl {
|
||||
padding: 20px;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.batchInfoUl li {
|
||||
line-height: 24px;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
}
|
||||
|
||||
.batchInfoUl li span:first-child {
|
||||
padding-right: 20px;
|
||||
width: 240px;
|
||||
display: inline-block;
|
||||
color: #666;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.batchInfoUl li span:last-child {
|
||||
width: 220px;
|
||||
display: inline-block;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.batchInfoUl li span i {
|
||||
display: inline-block;
|
||||
line-height: 1.2em;
|
||||
font-size: 0.8em;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.batchInfoUl li span a {
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
@ -18,14 +18,12 @@ import { ConfirmationMessage } from './confirmation-message';
|
||||
import { ConfirmationAcknowledgement } from './confirmation-state-message';
|
||||
import { ConfirmationState, ConfirmationTargets, ConfirmationButtons } from '../shared/shared.const';
|
||||
|
||||
import { CONFIRMATION_DIALOG_TEMPLATE } from './confirmation-dialog.component.html';
|
||||
import { CONFIRMATION_DIALOG_STYLE } from './confirmation-dialog.component.css';
|
||||
import {BatchInfo} from './confirmation-batch-message';
|
||||
|
||||
@Component({
|
||||
selector: 'confirmation-dialog',
|
||||
template: CONFIRMATION_DIALOG_TEMPLATE,
|
||||
styles: [ CONFIRMATION_DIALOG_STYLE ]
|
||||
templateUrl: './confirmation-dialog.component.html',
|
||||
styleUrls: [ './confirmation-dialog.component.scss' ]
|
||||
})
|
||||
|
||||
export class ConfirmationDialogComponent {
|
||||
|
@ -1,4 +1,3 @@
|
||||
export const CREATE_EDIT_ENDPOINT_TEMPLATE: string = `
|
||||
<clr-modal [(clrModalOpen)]="createEditDestinationOpened" [clrModalStaticBackdrop]="staticBackdrop" [clrModalClosable]="closable">
|
||||
<h3 class="modal-title">{{modalTitle}}</h3>
|
||||
<hbr-inline-alert class="modal-title" (confirmEvt)="confirmCancel($event)"></hbr-inline-alert>
|
||||
@ -61,4 +60,4 @@ export const CREATE_EDIT_ENDPOINT_TEMPLATE: string = `
|
||||
<button type="button" class="btn btn-outline" (click)="onCancel()" [disabled]="inProgress">{{ 'BUTTON.CANCEL' | translate }}</button>
|
||||
<button type="submit" class="btn btn-primary" (click)="onSubmit()" [disabled]="!isValid">{{ 'BUTTON.OK' | translate }}</button>
|
||||
</div>
|
||||
</clr-modal>`;
|
||||
</clr-modal>
|
@ -1,4 +1,3 @@
|
||||
export const CREATE_EDIT_ENDPOINT_STYLE = `
|
||||
.form-group-label-override {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
@ -7,5 +6,4 @@ export const CREATE_EDIT_ENDPOINT_STYLE = `
|
||||
clr-tooltip {
|
||||
top: 3px;
|
||||
position: relative;
|
||||
}
|
||||
`;
|
||||
}
|
@ -27,15 +27,15 @@ describe('CreateEditEndpointComponent (inline template)', () => {
|
||||
|
||||
let comp: CreateEditEndpointComponent;
|
||||
let fixture: ComponentFixture<CreateEditEndpointComponent>;
|
||||
|
||||
|
||||
let config: IServiceConfig = {
|
||||
systemInfoEndpoint: '/api/endpoints/testing'
|
||||
};
|
||||
|
||||
let endpointService: EndpointService;
|
||||
|
||||
|
||||
let spy: jasmine.Spy;
|
||||
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
@ -57,11 +57,11 @@ describe('CreateEditEndpointComponent (inline template)', () => {
|
||||
beforeEach(()=>{
|
||||
fixture = TestBed.createComponent(CreateEditEndpointComponent);
|
||||
comp = fixture.componentInstance;
|
||||
|
||||
|
||||
endpointService = fixture.debugElement.injector.get(EndpointService);
|
||||
spy = spyOn(endpointService, 'getEndpoint').and.returnValue(Promise.resolve(mockData));
|
||||
fixture.detectChanges();
|
||||
|
||||
|
||||
comp.openCreateEditTarget(true, 1);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
@ -32,9 +32,6 @@ import { Endpoint } from '../service/interface';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { CREATE_EDIT_ENDPOINT_STYLE } from './create-edit-endpoint.component.css';
|
||||
import { CREATE_EDIT_ENDPOINT_TEMPLATE } from './create-edit-endpoint.component.html';
|
||||
|
||||
import { toPromise, clone, compareValue, isEmptyObject } from '../utils';
|
||||
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
@ -43,8 +40,8 @@ const FAKE_PASSWORD = 'rjGcfuRu';
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-create-edit-endpoint',
|
||||
template: CREATE_EDIT_ENDPOINT_TEMPLATE,
|
||||
styles: [CREATE_EDIT_ENDPOINT_STYLE]
|
||||
templateUrl: './create-edit-endpoint.component.html',
|
||||
styleUrls: ['./create-edit-endpoint.component.scss']
|
||||
})
|
||||
export class CreateEditEndpointComponent implements AfterViewChecked, OnDestroy {
|
||||
modalTitle: string;
|
||||
|
@ -1,30 +0,0 @@
|
||||
export const CREATE_EDIT_LABEL_STYLE: string = `
|
||||
.form-group-label-override {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
form{margin-bottom:-10px;padding-top:0; margin-top: 20px;width: 100%;background-color: #eee; border:1px solid #ccc;}
|
||||
form .form-group{display:inline-flex;padding-left: 70px;}
|
||||
form .form-group>label:first-child{width: auto;}
|
||||
section{padding:.5rem 0;}
|
||||
section> label{margin-left: 20px;}
|
||||
|
||||
.colorDrop {display:inline-block;position: relative; width: 132px;}
|
||||
.colorDrop .colorPanel{position:absolute; width:166px; padding:6px; background-color: white; border: 1px solid #ccc; z-index:10;}
|
||||
.btnColor{
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
width: 26px;
|
||||
height:22px;
|
||||
min-width: 26px;}
|
||||
.colorPanel span{margin: 5px 4px; width:30px;height:24px; text-align: center;line-height: 24px;font-size:12px; border:1px solid #A1A1A1;}
|
||||
.closePanel{ display: block;
|
||||
left: 138px;
|
||||
position: relative;
|
||||
font-size: 18px;
|
||||
width: 10px;
|
||||
line-height: 8px;
|
||||
cursor: pointer;
|
||||
text-decoration: none;}
|
||||
`;
|
@ -1,4 +1,3 @@
|
||||
export const CREATE_EDIT_LABEL_TEMPLATE: string = `
|
||||
<div>
|
||||
<form #labelForm="ngForm" [hidden]="!formShow">
|
||||
<section>
|
||||
@ -34,4 +33,4 @@ export const CREATE_EDIT_LABEL_TEMPLATE: string = `
|
||||
</label>
|
||||
</section>
|
||||
</form>
|
||||
</div>`;
|
||||
</div>
|
@ -0,0 +1,74 @@
|
||||
.form-group-label-override {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
form {
|
||||
margin-bottom: -10px;
|
||||
padding-top: 0;
|
||||
margin-top: 20px;
|
||||
width: 100%;
|
||||
background-color: #eee;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
form .form-group {
|
||||
display: inline-flex;
|
||||
padding-left: 70px;
|
||||
}
|
||||
|
||||
form .form-group>label:first-child {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
section {
|
||||
padding: .5rem 0;
|
||||
}
|
||||
|
||||
section>label {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.colorDrop {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 132px;
|
||||
}
|
||||
|
||||
.colorDrop .colorPanel {
|
||||
position: absolute;
|
||||
width: 166px;
|
||||
padding: 6px;
|
||||
background-color: white;
|
||||
border: 1px solid #ccc;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.btnColor {
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
width: 26px;
|
||||
height: 22px;
|
||||
min-width: 26px;
|
||||
}
|
||||
|
||||
.colorPanel span {
|
||||
margin: 5px 4px;
|
||||
width: 30px;
|
||||
height: 24px;
|
||||
text-align: center;
|
||||
line-height: 24px;
|
||||
font-size: 12px;
|
||||
border: 1px solid #A1A1A1;
|
||||
}
|
||||
|
||||
.closePanel {
|
||||
display: block;
|
||||
left: 138px;
|
||||
position: relative;
|
||||
font-size: 18px;
|
||||
width: 10px;
|
||||
line-height: 8px;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
@ -19,12 +19,8 @@ import {
|
||||
Input, OnInit, ViewChild, ChangeDetectionStrategy, ChangeDetectorRef
|
||||
} from '@angular/core';
|
||||
|
||||
|
||||
import {Label} from '../service/interface';
|
||||
|
||||
import { CREATE_EDIT_LABEL_STYLE } from './create-edit-label.component.css';
|
||||
import { CREATE_EDIT_LABEL_TEMPLATE } from './create-edit-label.component.html';
|
||||
|
||||
import {toPromise, clone, compareValue} from '../utils';
|
||||
|
||||
import {LabelService} from "../service/label.service";
|
||||
@ -35,8 +31,8 @@ import {LabelColor} from "../shared/shared.const";
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-create-edit-label',
|
||||
template: CREATE_EDIT_LABEL_TEMPLATE,
|
||||
styles: [CREATE_EDIT_LABEL_STYLE],
|
||||
templateUrl: './create-edit-label.component.html',
|
||||
styleUrls: ['./create-edit-label.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.Default
|
||||
})
|
||||
|
||||
|
@ -1,70 +0,0 @@
|
||||
export const CREATE_EDIT_RULE_STYLE: string = `
|
||||
/**
|
||||
* Created by pengf on 9/28/2017.
|
||||
*/
|
||||
|
||||
.select{
|
||||
width: 186px;
|
||||
}
|
||||
.select .optionMore{
|
||||
background-color: #bfbaba;
|
||||
height: 1.6em;
|
||||
font-size: 1.2em;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
}
|
||||
.hideFilter{ display: none;}
|
||||
h4{
|
||||
color: #666;
|
||||
}
|
||||
.colorRed{color: red;}
|
||||
.colorRed a{text-decoration: underline;color: #007CBB;}
|
||||
.alertLabel{display:block; margin-top:2px; line-height:1em; font-size:12px;}
|
||||
|
||||
.inputWidth{width: 270px;}
|
||||
.endpointSelect{ width: 270px; margin-right: 20px;}
|
||||
.filterSelect{width: 315px;}
|
||||
.filterSelect clr-icon{margin-left: 15px;}
|
||||
.filterSelect label{width: 136px;}
|
||||
.filterSelect label input{width: 100%;}
|
||||
.pull-left{float: left;}
|
||||
.padLeft0{padding-left: 0;}
|
||||
.floatSetPar{display: inline-block; width: 120px;margin-right: 10px;}
|
||||
.floatSet {display: inline-block; width: 82px;margin-right: 4px;}
|
||||
.form-group{ min-height: 36px;}
|
||||
|
||||
.projectInput{float: left;position: relative;}
|
||||
.switchIcon{width:20px;height:20px; margin-top: 10px;margin-left: 10px; cursor: pointer;}
|
||||
.addEndpoint{ margin-top: .25em !important;padding-left:2px;padding-right:2px;min-width:58px;margin-right:0}
|
||||
.shadow{position: absolute;top: 8px;}
|
||||
.is-solid{cursor: pointer;}
|
||||
.selectBox{
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
margin-top:-0.25rem;
|
||||
border: 1px solid #ccc;
|
||||
background-color: white;
|
||||
border: 1px solid rgba(0,0,0,.15);
|
||||
border-right-width: 2px;
|
||||
border-bottom-width: 2px;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 5px 10px rgba(0,0,0,.2);
|
||||
z-index: 100;
|
||||
}
|
||||
.selectBox ul li{
|
||||
list-style: none;
|
||||
padding: 3px 20px
|
||||
cursor: pointer;
|
||||
}
|
||||
.selectBox ul li:hover{
|
||||
color: #262626;
|
||||
background-image: linear-gradient(180deg,#f5f5f5 0,#e8e8e8);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.form-group-override{
|
||||
padding-left: 170px !important;
|
||||
}
|
||||
.form-group>label:first-child{font-size:14px; width:6.5rem;}
|
||||
.goLink{color:blue; border-bottom:1px solid blue; line-height:14px; cursor:pointer;}
|
||||
`;
|
@ -1,4 +1,3 @@
|
||||
export const CREATE_EDIT_RULE_TEMPLATE: string = `
|
||||
<clr-modal [(clrModalOpen)]="createEditRuleOpened" [clrModalStaticBackdrop]="true" [clrModalClosable]="false">
|
||||
<h3 class="modal-title">{{headerTitle | translate}}</h3>
|
||||
<hbr-inline-alert class="modal-title" (confirmEvt)="confirmCancel($event)"></hbr-inline-alert>
|
||||
@ -131,4 +130,4 @@ export const CREATE_EDIT_RULE_TEMPLATE: string = `
|
||||
<button type="button" id="ruleBtnCancel" class="btn btn-outline" [disabled]="this.inProgress" (click)="onCancel()">{{ 'BUTTON.CANCEL' | translate }}</button>
|
||||
<button type="submit" id="ruleBtnOk" class="btn btn-primary" (click)="onSubmit()" [disabled]="!ruleForm.valid || !isValid || !hasFormChange()">{{ 'BUTTON.SAVE' | translate }}</button>
|
||||
</div>
|
||||
</clr-modal>`;
|
||||
</clr-modal>
|
@ -0,0 +1,157 @@
|
||||
.select {
|
||||
width: 186px;
|
||||
}
|
||||
|
||||
.select .optionMore {
|
||||
background-color: #bfbaba;
|
||||
height: 1.6em;
|
||||
font-size: 1.2em;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hideFilter {
|
||||
display: none;
|
||||
}
|
||||
|
||||
h4 {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.colorRed {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.colorRed a {
|
||||
text-decoration: underline;
|
||||
color: #007CBB;
|
||||
}
|
||||
|
||||
.alertLabel {
|
||||
display: block;
|
||||
margin-top: 2px;
|
||||
line-height: 1em;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.inputWidth {
|
||||
width: 270px;
|
||||
}
|
||||
|
||||
.endpointSelect {
|
||||
width: 270px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.filterSelect {
|
||||
width: 315px;
|
||||
}
|
||||
|
||||
.filterSelect clr-icon {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.filterSelect label {
|
||||
width: 136px;
|
||||
}
|
||||
|
||||
.filterSelect label input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pull-left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.padLeft0 {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.floatSetPar {
|
||||
display: inline-block;
|
||||
width: 120px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.floatSet {
|
||||
display: inline-block;
|
||||
width: 82px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
min-height: 36px;
|
||||
}
|
||||
|
||||
.projectInput {
|
||||
float: left;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.switchIcon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-top: 10px;
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.addEndpoint {
|
||||
margin-top: .25em !important;
|
||||
padding-left: 2px;
|
||||
padding-right: 2px;
|
||||
min-width: 58px;
|
||||
margin-right: 0
|
||||
}
|
||||
|
||||
.shadow {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
}
|
||||
|
||||
.is-solid {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.selectBox {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
margin-top: -0.25rem;
|
||||
border: 1px solid #ccc;
|
||||
background-color: white;
|
||||
border: 1px solid rgba(0, 0, 0, .15);
|
||||
border-right-width: 2px;
|
||||
border-bottom-width: 2px;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.selectBox ul li {
|
||||
list-style: none;
|
||||
padding: 3px 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.selectBox ul li:hover {
|
||||
color: #262626;
|
||||
background-image: linear-gradient(180deg, #f5f5f5 0, #e8e8e8);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
|
||||
.form-group-override {
|
||||
padding-left: 170px !important;
|
||||
}
|
||||
|
||||
.form-group>label:first-child {
|
||||
font-size: 14px;
|
||||
width: 6.5rem;
|
||||
}
|
||||
|
||||
.goLink {
|
||||
color: blue;
|
||||
border-bottom: 1px solid blue;
|
||||
line-height: 14px;
|
||||
cursor: pointer;
|
||||
}
|
@ -21,8 +21,6 @@ import {Router, ActivatedRoute} from "@angular/router";
|
||||
import {compareValue, isEmptyObject, toPromise} from "../utils";
|
||||
import { InlineAlertComponent } from '../inline-alert/inline-alert.component';
|
||||
import {ReplicationService} from "../service/replication.service";
|
||||
import {CREATE_EDIT_RULE_TEMPLATE} from "./create-edit-rule.component.html";
|
||||
import {CREATE_EDIT_RULE_STYLE} from "./create-edit-rule.component.css";
|
||||
import {ErrorHandler} from "../error-handler/error-handler";
|
||||
import {TranslateService} from "@ngx-translate/core";
|
||||
import {EndpointService} from "../service/endpoint.service";
|
||||
@ -34,8 +32,8 @@ const ONE_DAY_SECONDS: number = 24 * ONE_HOUR_SECONDS;
|
||||
|
||||
@Component ({
|
||||
selector: 'hbr-create-edit-rule',
|
||||
template: CREATE_EDIT_RULE_TEMPLATE,
|
||||
styles: [CREATE_EDIT_RULE_STYLE]
|
||||
templateUrl: './create-edit-rule.component.html',
|
||||
styleUrls: ['./create-edit-rule.component.scss']
|
||||
|
||||
})
|
||||
|
||||
|
@ -1,9 +1,7 @@
|
||||
export const DATETIME_PICKER_TEMPLATE: string = `
|
||||
<clr-icon shape="date"></clr-icon>
|
||||
<label aria-haspopup="true" role="tooltip" [class.invalid]="dateInvalid" class="tooltip tooltip-validation tooltip-sm">
|
||||
<input type="date" #searchTime="ngModel" [(ngModel)]="dateInput" name="searchTime" placeholder="dd/mm/yyyy" dateValidator (change)="doSearch()">
|
||||
<span *ngIf="dateInvalid" class="tooltip-content">
|
||||
{{'AUDIT_LOG.INVALID_DATE' | translate }}
|
||||
</span>
|
||||
</label>
|
||||
`;
|
||||
</label>
|
@ -1,11 +1,9 @@
|
||||
import {Component, Input, Output, EventEmitter, ViewChild, OnChanges} from '@angular/core';
|
||||
import { NgModel } from '@angular/forms';
|
||||
|
||||
import { DATETIME_PICKER_TEMPLATE } from './datetime-picker.component.html';
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-datetime',
|
||||
template: DATETIME_PICKER_TEMPLATE
|
||||
templateUrl: './datetime-picker.component.html'
|
||||
})
|
||||
export class DatePickerComponent implements OnChanges{
|
||||
|
||||
|
@ -1,21 +0,0 @@
|
||||
export const ENDPOINT_STYLE: string = `
|
||||
.option-left {
|
||||
padding-left: 16px;
|
||||
margin-top: -6px;
|
||||
}
|
||||
.option-right {
|
||||
padding-right: 16px;
|
||||
}
|
||||
.refresh-btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
.refresh-btn:hover {
|
||||
color: #007CBB;
|
||||
}
|
||||
.rightPos{
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
right: 35px;
|
||||
margin-top: 4px;
|
||||
height: 24px;}
|
||||
`;
|
@ -1,4 +1,3 @@
|
||||
export const ENDPOINT_TEMPLATE = `
|
||||
<div>
|
||||
<div class="row" style="position:relative;">
|
||||
<div>
|
||||
@ -41,5 +40,4 @@ export const ENDPOINT_TEMPLATE = `
|
||||
</div>
|
||||
<confirmation-dialog #confirmationDialog [batchInfors]="batchDelectionInfos" (confirmAction)="confirmDeletion($event)"></confirmation-dialog>
|
||||
<hbr-create-edit-endpoint (reload)="reload($event)"></hbr-create-edit-endpoint>
|
||||
</div>
|
||||
`;
|
||||
</div>
|
24
src/ui_ng/lib/src/endpoint/endpoint.component.scss
Normal file
24
src/ui_ng/lib/src/endpoint/endpoint.component.scss
Normal file
@ -0,0 +1,24 @@
|
||||
.option-left {
|
||||
padding-left: 16px;
|
||||
margin-top: -6px;
|
||||
}
|
||||
|
||||
.option-right {
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.refresh-btn:hover {
|
||||
color: #007CBB;
|
||||
}
|
||||
|
||||
.rightPos {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
right: 35px;
|
||||
margin-top: 4px;
|
||||
height: 24px;
|
||||
}
|
@ -29,9 +29,6 @@ import { Subscription } from 'rxjs/Subscription';
|
||||
|
||||
import { CreateEditEndpointComponent } from '../create-edit-endpoint/create-edit-endpoint.component';
|
||||
|
||||
import { ENDPOINT_STYLE } from './endpoint.component.css';
|
||||
import { ENDPOINT_TEMPLATE } from './endpoint.component.html';
|
||||
|
||||
import { toPromise, CustomComparator } from '../utils';
|
||||
|
||||
import { State, Comparator } from 'clarity-angular';
|
||||
@ -40,8 +37,8 @@ import {Observable} from "rxjs/Observable";
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-endpoint',
|
||||
template: ENDPOINT_TEMPLATE,
|
||||
styles: [ENDPOINT_STYLE],
|
||||
templateUrl: './endpoint.component.html',
|
||||
styleUrls: ['./endpoint.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class EndpointComponent implements OnInit, OnDestroy {
|
||||
|
6
src/ui_ng/lib/src/filter/filter.component.html
Normal file
6
src/ui_ng/lib/src/filter/filter.component.html
Normal file
@ -0,0 +1,6 @@
|
||||
<span>
|
||||
<clr-icon shape="search" size="20" class="search-btn" [class.filter-icon]="isShowSearchBox" (click)="onClick()"></clr-icon>
|
||||
<input [hidden]="!isShowSearchBox" type="text" style="padding-left: 15px;" (keyup)="valueChange()" (focus)="inputFocus()"
|
||||
placeholder="{{placeHolder}}" [(ngModel)]="currentValue" />
|
||||
<span class="filter-divider" *ngIf="withDivider"></span>
|
||||
</span>
|
25
src/ui_ng/lib/src/filter/filter.component.scss
Normal file
25
src/ui_ng/lib/src/filter/filter.component.scss
Normal file
@ -0,0 +1,25 @@
|
||||
.filter-icon {
|
||||
position: relative;
|
||||
right: -12px;
|
||||
}
|
||||
|
||||
.filter-divider {
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 2px;
|
||||
background-color: #cccccc;
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
position: relative;
|
||||
top: 9px;
|
||||
margin-right: 6px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.search-btn:hover {
|
||||
color: #007CBB;
|
||||
}
|
@ -18,13 +18,11 @@ import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/operator/debounceTime';
|
||||
import 'rxjs/add/operator/distinctUntilChanged';
|
||||
|
||||
import { FILTER_TEMPLATE, FILTER_STYLES } from './filter.template';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-filter',
|
||||
styles: [FILTER_STYLES],
|
||||
template: FILTER_TEMPLATE
|
||||
templateUrl: './filter.component.html',
|
||||
styleUrls: ['./filter.component.scss']
|
||||
})
|
||||
|
||||
export class FilterComponent implements OnInit {
|
||||
|
@ -1,39 +0,0 @@
|
||||
/**
|
||||
* Define template resources for filter component
|
||||
*/
|
||||
|
||||
export const FILTER_TEMPLATE: string = `
|
||||
<span>
|
||||
<clr-icon shape="search" size="20" class="search-btn" [class.filter-icon]="isShowSearchBox" (click)="onClick()"></clr-icon>
|
||||
<input [hidden]="!isShowSearchBox" type="text" style="padding-left: 15px;" (keyup)="valueChange()" (focus)="inputFocus()" placeholder="{{placeHolder}}" [(ngModel)]="currentValue"/>
|
||||
<span class="filter-divider" *ngIf="withDivider"></span>
|
||||
</span>
|
||||
`;
|
||||
|
||||
export const FILTER_STYLES: string = `
|
||||
.filter-icon {
|
||||
position: relative;
|
||||
right: -12px;
|
||||
}
|
||||
|
||||
.filter-divider {
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 2px;
|
||||
background-color: #cccccc;
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
position: relative;
|
||||
top: 9px;
|
||||
margin-right: 6px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.search-btn:hover {
|
||||
color: #007CBB;
|
||||
}
|
||||
`;
|
@ -1,4 +1,3 @@
|
||||
export const GRIDVIEW_TEMPLATE = `
|
||||
<div class="grid-content" (scroll)="onScroll($event)">
|
||||
<div class="items" [ngStyle]="itemsHolderStyle" #itemsHolder >
|
||||
<span *ngFor="let item of items;let i = index; trackBy:trackByFn" class='card-item' [ngStyle]="cardStyles[i]" #cardItem
|
||||
@ -14,5 +13,4 @@ export const GRIDVIEW_TEMPLATE = `
|
||||
<span class="vertical-helper"></span>
|
||||
<div class="spinner"></div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
</div>
|
@ -1,10 +1,3 @@
|
||||
// Copyright (c) 2017-2018 VMware, Inc. All Rights Reserved.
|
||||
// This software is released under MIT license.
|
||||
// The full license information can be found in LICENSE in the root directory of this project.
|
||||
|
||||
// @import 'node_modules/admiral-ui-common/css/mixins';
|
||||
|
||||
export const GRIDVIEW_STYLE = `
|
||||
.grid-content {
|
||||
position: relative;
|
||||
top: 36px;
|
||||
@ -37,10 +30,10 @@ export const GRIDVIEW_STYLE = `
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
@include animation(fadein 0.4s);
|
||||
text-align: center;
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.central-block-loading-more {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
@ -48,10 +41,10 @@ export const GRIDVIEW_STYLE = `
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
@include animation(fadein 0.4s);
|
||||
text-align: center;
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.vertical-helper {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
@ -62,6 +55,4 @@ export const GRIDVIEW_STYLE = `
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
`
|
||||
}
|
@ -16,14 +16,12 @@ import { Router, ActivatedRoute, NavigationEnd } from '@angular/router';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { GRIDVIEW_TEMPLATE } from './grid-view.component.html';
|
||||
import { GRIDVIEW_STYLE } from './grid-view.component.css';
|
||||
import { ScrollPosition } from '../service/interface'
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-gridview',
|
||||
template: GRIDVIEW_TEMPLATE,
|
||||
styles: [GRIDVIEW_STYLE],
|
||||
templateUrl: './grid-view.component.html',
|
||||
styleUrls: ['./grid-view.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
/**
|
||||
|
@ -4,9 +4,6 @@ import { LOG_DIRECTIVES } from './log/index';
|
||||
import { FILTER_DIRECTIVES } from './filter/index';
|
||||
import { ENDPOINT_DIRECTIVES } from './endpoint/index';
|
||||
import { REPOSITORY_DIRECTIVES } from './repository/index';
|
||||
import { REPOSITORY_STACKVIEW_DIRECTIVES } from './repository-stackview/index';
|
||||
|
||||
import { REPOSITORY_LISTVIEW_DIRECTIVES } from './repository-listview/index';
|
||||
import { TAG_DIRECTIVES } from './tag/index';
|
||||
|
||||
import { REPLICATION_DIRECTIVES } from './replication/index';
|
||||
@ -167,8 +164,6 @@ export function initConfig(translateInitializer: TranslateServiceInitializer, co
|
||||
FILTER_DIRECTIVES,
|
||||
ENDPOINT_DIRECTIVES,
|
||||
REPOSITORY_DIRECTIVES,
|
||||
REPOSITORY_STACKVIEW_DIRECTIVES,
|
||||
REPOSITORY_LISTVIEW_DIRECTIVES,
|
||||
TAG_DIRECTIVES,
|
||||
CREATE_EDIT_ENDPOINT_DIRECTIVES,
|
||||
CONFIRMATION_DIALOG_DIRECTIVES,
|
||||
@ -193,8 +188,6 @@ export function initConfig(translateInitializer: TranslateServiceInitializer, co
|
||||
FILTER_DIRECTIVES,
|
||||
ENDPOINT_DIRECTIVES,
|
||||
REPOSITORY_DIRECTIVES,
|
||||
REPOSITORY_STACKVIEW_DIRECTIVES,
|
||||
REPOSITORY_LISTVIEW_DIRECTIVES,
|
||||
TAG_DIRECTIVES,
|
||||
CREATE_EDIT_ENDPOINT_DIRECTIVES,
|
||||
CONFIRMATION_DIALOG_DIRECTIVES,
|
||||
|
@ -9,7 +9,6 @@ export * from './endpoint/index';
|
||||
export * from './repository/index';
|
||||
export * from './create-edit-endpoint/index';
|
||||
export * from './create-edit-rule/index';
|
||||
export * from './repository-stackview/index';
|
||||
export * from './tag/index';
|
||||
export * from './list-replication-rule/index';
|
||||
export * from './replication/index';
|
||||
|
@ -1,4 +1,3 @@
|
||||
export const INLINE_ALERT_TEMPLATE: string = `
|
||||
<clr-alert [clrAlertType]="inlineAlertType" [clrAlertClosable]="inlineAlertClosable" [(clrAlertClosed)]="alertClose" [clrAlertAppLevel]="useAppLevelStyle">
|
||||
<div class="alert-item">
|
||||
<span class="alert-text" [class.alert-text-blink]="blinking">
|
||||
@ -9,5 +8,4 @@ export const INLINE_ALERT_TEMPLATE: string = `
|
||||
<button class="btn btn-sm btn-link alert-btn-link" (click)="confirmCancel()">{{'BUTTON.YES' | translate}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</clr-alert>
|
||||
`;
|
||||
</clr-alert>
|
@ -1,4 +1,3 @@
|
||||
export const INLINE_ALERT_STYLE: string = `
|
||||
.alert-text-blink {
|
||||
color: red;
|
||||
font-weight: bolder;
|
||||
@ -12,5 +11,4 @@ export const INLINE_ALERT_STYLE: string = `
|
||||
}
|
||||
:host >>> .alert-icon-wrapper{
|
||||
display: inline;
|
||||
}
|
||||
`;
|
||||
}
|
@ -18,13 +18,10 @@ import { errorHandler } from '../shared/shared.utils';
|
||||
import { Observable } from 'rxjs/Rx';
|
||||
import { Subscription } from "rxjs";
|
||||
|
||||
import { INLINE_ALERT_STYLE } from './inline-alert.component.css';
|
||||
import { INLINE_ALERT_TEMPLATE } from './inline-alert.component.html';
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-inline-alert',
|
||||
template: INLINE_ALERT_TEMPLATE,
|
||||
styles: [ INLINE_ALERT_STYLE ]
|
||||
templateUrl: './inline-alert.component.html',
|
||||
styleUrls: [ './inline-alert.component.scss' ]
|
||||
})
|
||||
export class InlineAlertComponent {
|
||||
inlineAlertType: string = 'alert-danger';
|
||||
|
@ -1,4 +1,3 @@
|
||||
export const JOB_LOG_VIEWER_TEMPLATE: string = `
|
||||
<clr-modal [(clrModalOpen)]="opened" [clrModalStaticBackdrop]="true" [clrModalSize]="'xl'">
|
||||
<h3 class="modal-title" class="log-viewer-title" style="margin-top: 0px;">{{title | translate }}</h3>
|
||||
<div class="modal-body">
|
||||
@ -12,20 +11,4 @@ export const JOB_LOG_VIEWER_TEMPLATE: string = `
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" (click)="close()">{{ 'BUTTON.CLOSE' | translate}}</button>
|
||||
</div>
|
||||
</clr-modal>
|
||||
`;
|
||||
|
||||
export const JOB_LOG_VIEWER_STYLES: string = `
|
||||
.log-viewer-title {
|
||||
line-height: 24px;
|
||||
color: #000000;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.loading-back {
|
||||
height: 358px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
`;
|
||||
</clr-modal>
|
@ -0,0 +1,12 @@
|
||||
.log-viewer-title {
|
||||
line-height: 24px;
|
||||
color: #000000;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.loading-back {
|
||||
height: 358px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
@ -13,7 +13,6 @@
|
||||
// limitations under the License.
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, Input} from '@angular/core';
|
||||
|
||||
import { JOB_LOG_VIEWER_TEMPLATE, JOB_LOG_VIEWER_STYLES } from './job-log-viewer.component.template';
|
||||
import { JobLogService } from '../service/index';
|
||||
import { ErrorHandler } from '../error-handler/index';
|
||||
import { toPromise } from '../utils';
|
||||
@ -22,8 +21,8 @@ const supportSet: string[] = ["replication", "scan"];
|
||||
|
||||
@Component({
|
||||
selector: 'job-log-viewer',
|
||||
template: JOB_LOG_VIEWER_TEMPLATE,
|
||||
styles: [JOB_LOG_VIEWER_STYLES],
|
||||
templateUrl: './job-log-viewer.component.html',
|
||||
styleUrls: ['./job-log-viewer.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
|
||||
|
5
src/ui_ng/lib/src/label-piece/label-piece.component.html
Normal file
5
src/ui_ng/lib/src/label-piece/label-piece.component.html
Normal file
@ -0,0 +1,5 @@
|
||||
<label class="label" [ngStyle]="{'background-color': labelColor?.color, 'color': labelColor?.textColor, 'border': labelColor?.color == '#FFFFFF'? '1px solid #A1A1A1': 'none'}" [style.max-width.px]="labelWidth">
|
||||
<clr-icon *ngIf="label.scope=='p'" shape="organization"></clr-icon>
|
||||
<clr-icon *ngIf="label.scope=='g'" shape="administrator"></clr-icon>
|
||||
{{label.name}}
|
||||
</label>
|
17
src/ui_ng/lib/src/label-piece/label-piece.component.scss
Normal file
17
src/ui_ng/lib/src/label-piece/label-piece.component.scss
Normal file
@ -0,0 +1,17 @@
|
||||
.label {
|
||||
border: none;
|
||||
color: #222;
|
||||
display: inline-block;
|
||||
justify-content: flex-start;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
line-height: .875rem;
|
||||
}
|
||||
|
||||
.label clr-icon {
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.btn-group .dropdown-menu clr-icon {
|
||||
display: block;
|
||||
}
|
@ -18,15 +18,14 @@ import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/operator/debounceTime';
|
||||
import 'rxjs/add/operator/distinctUntilChanged';
|
||||
|
||||
import { LABEL_PIEICE_TEMPLATE, LABEL_PIEICE_STYLES } from './label-piece.template';
|
||||
import {Label} from "../service/interface";
|
||||
import {LabelColor} from "../shared/shared.const";
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-label-piece',
|
||||
styles: [LABEL_PIEICE_STYLES],
|
||||
template: LABEL_PIEICE_TEMPLATE
|
||||
templateUrl: './label-piece.component.html',
|
||||
styleUrls: ['./label-piece.component.scss']
|
||||
})
|
||||
|
||||
export class LabelPieceComponent implements OnInit, OnChanges {
|
||||
|
@ -1,22 +0,0 @@
|
||||
/**
|
||||
* Define template resources for filter component
|
||||
*/
|
||||
|
||||
export const LABEL_PIEICE_TEMPLATE: string = `
|
||||
<label class="label" [ngStyle]="{'background-color': labelColor?.color, 'color': labelColor?.textColor, 'border': labelColor?.color == '#FFFFFF'? '1px solid #A1A1A1': 'none'}" [style.max-width.px]="labelWidth">
|
||||
<clr-icon *ngIf="label.scope=='p'" shape="organization"></clr-icon>
|
||||
<clr-icon *ngIf="label.scope=='g'" shape="administrator"></clr-icon>
|
||||
{{label.name}}
|
||||
</label>
|
||||
`;
|
||||
|
||||
export const LABEL_PIEICE_STYLES: string = `
|
||||
.label{border: none; color:#222;
|
||||
display: inline-block;
|
||||
justify-content: flex-start;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
line-height: .875rem;}
|
||||
.label clr-icon{ margin-right: 3px;}
|
||||
.btn-group .dropdown-menu clr-icon{display:block;}
|
||||
`;
|
@ -1,21 +0,0 @@
|
||||
export const LABEL_STYLE: string = `
|
||||
.option-left {
|
||||
padding-left: 16px;
|
||||
margin-top: -6px;
|
||||
}
|
||||
.option-right {
|
||||
padding-right: 16px;
|
||||
}
|
||||
.refresh-btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
.refresh-btn:hover {
|
||||
color: #007CBB;
|
||||
}
|
||||
.rightPos{
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
right: 35px;
|
||||
margin-top: 4px;
|
||||
height: 24px;}
|
||||
`;
|
@ -1,4 +1,3 @@
|
||||
export const LABEL_TEMPLATE = `
|
||||
<div>
|
||||
<div class="row" style="position:relative;">
|
||||
<div>
|
||||
@ -39,5 +38,4 @@ export const LABEL_TEMPLATE = `
|
||||
</div>
|
||||
</div>
|
||||
<confirmation-dialog #confirmationDialog [batchInfors]="batchDelectionInfos" (confirmAction)="confirmDeletion($event)"></confirmation-dialog>
|
||||
</div>
|
||||
`;
|
||||
</div>
|
24
src/ui_ng/lib/src/label/label.component.scss
Normal file
24
src/ui_ng/lib/src/label/label.component.scss
Normal file
@ -0,0 +1,24 @@
|
||||
.option-left {
|
||||
padding-left: 16px;
|
||||
margin-top: -6px;
|
||||
}
|
||||
|
||||
.option-right {
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.refresh-btn:hover {
|
||||
color: #007CBB;
|
||||
}
|
||||
|
||||
.rightPos {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
right: 35px;
|
||||
margin-top: 4px;
|
||||
height: 24px;
|
||||
}
|
@ -15,8 +15,6 @@ import {
|
||||
Component, OnInit, OnDestroy, ViewChild, ChangeDetectionStrategy, ChangeDetectorRef,
|
||||
Input
|
||||
} from '@angular/core';
|
||||
import {LABEL_TEMPLATE} from "./label.component.html";
|
||||
import {LABEL_STYLE} from "./label.component.css";
|
||||
import {Label} from "../service/interface";
|
||||
import {LabelDefaultService, LabelService} from "../service/label.service";
|
||||
import {toPromise} from "../utils";
|
||||
@ -30,8 +28,8 @@ import {TranslateService} from "@ngx-translate/core";
|
||||
import {ConfirmationDialogComponent} from "../confirmation-dialog/confirmation-dialog.component";
|
||||
@Component({
|
||||
selector: 'hbr-label',
|
||||
template: LABEL_TEMPLATE,
|
||||
styles: [LABEL_STYLE],
|
||||
templateUrl: './label.component.html',
|
||||
styleUrls: ['./label.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class LabelComponent implements OnInit {
|
||||
|
@ -1,2 +0,0 @@
|
||||
export const LIST_REPLICATION_RULE_CSS = `
|
||||
`
|
@ -1,4 +1,3 @@
|
||||
export const LIST_REPLICATION_RULE_TEMPLATE: string = `
|
||||
<div style="padding-bottom: 15px;">
|
||||
<clr-datagrid [clrDgLoading]="loading" [(clrDgSingleSelected)]="selectedRow" [clDgRowSelection]="true">
|
||||
<clr-dg-action-bar style="height: 24px;">
|
||||
@ -36,5 +35,4 @@ export const LIST_REPLICATION_RULE_TEMPLATE: string = `
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
<confirmation-dialog #deletionConfirmDialog [batchInfors]="batchDelectionInfos" (confirmAction)="deletionConfirm($event)"></confirmation-dialog>
|
||||
</div>
|
||||
`;
|
||||
</div>
|
@ -41,15 +41,13 @@ import { toPromise, CustomComparator } from '../utils';
|
||||
|
||||
import { State, Comparator } from 'clarity-angular';
|
||||
|
||||
import { LIST_REPLICATION_RULE_TEMPLATE } from './list-replication-rule.component.html';
|
||||
import { LIST_REPLICATION_RULE_CSS } from './list-replication-rule.component.css';
|
||||
import {BatchInfo, BathInfoChanges} from "../confirmation-dialog/confirmation-batch-message";
|
||||
import {Observable} from "rxjs/Observable";
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-list-replication-rule',
|
||||
template: LIST_REPLICATION_RULE_TEMPLATE,
|
||||
styles: [LIST_REPLICATION_RULE_CSS],
|
||||
templateUrl: './list-replication-rule.component.html',
|
||||
styleUrls: ['./list-replication-rule.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ListReplicationRuleComponent implements OnInit, OnChanges {
|
||||
|
@ -1,8 +1,3 @@
|
||||
/**
|
||||
* Define the inline template and styles with ts variables
|
||||
*/
|
||||
|
||||
export const LOG_TEMPLATE: string = `
|
||||
<div>
|
||||
<h2 class="h2-log-override" *ngIf="withTitle">{{'SIDE_NAV.LOGS' | translate}}</h2>
|
||||
<div class="row flex-items-xs-between flex-items-xs-bottom">
|
||||
@ -16,10 +11,11 @@ export const LOG_TEMPLATE: string = `
|
||||
<option value="operation">{{"AUDIT_LOG.OPERATION" | translate | lowercase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<hbr-filter [withDivider]="true" filterPlaceholder='{{"AUDIT_LOG.FILTER_PLACEHOLDER" | translate}}' (filter)="doFilter($event)" (openFlag)="openFilter($event)" [currentValue]="currentTerm"></hbr-filter>
|
||||
<hbr-filter [withDivider]="true" filterPlaceholder='{{"AUDIT_LOG.FILTER_PLACEHOLDER" | translate}}' (filter)="doFilter($event)"
|
||||
(openFlag)="openFilter($event)" [currentValue]="currentTerm"></hbr-filter>
|
||||
<span (click)="refresh()" class="refresh-btn">
|
||||
<clr-icon shape="refresh" [hidden]="inProgress" ng-disabled="inProgress"></clr-icon>
|
||||
<span class="spinner spinner-inline" [hidden]="!inProgress"></span>
|
||||
<clr-icon shape="refresh" [hidden]="inProgress" ng-disabled="inProgress"></clr-icon>
|
||||
<span class="spinner spinner-inline" [hidden]="!inProgress"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -39,64 +35,10 @@ export const LOG_TEMPLATE: string = `
|
||||
<clr-dg-cell>{{l.op_time | date: 'short'}}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
||||
{{'AUDIT_LOG.OF' | translate}} {{pagination.totalItems}} {{'AUDIT_LOG.ITEMS' | translate}}
|
||||
<clr-dg-pagination #pagination [(clrDgPage)]="currentPage" [clrDgPageSize]="pageSize" [clrDgTotalItems]="totalCount"></clr-dg-pagination>
|
||||
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'AUDIT_LOG.OF' | translate}} {{pagination.totalItems}} {{'AUDIT_LOG.ITEMS'
|
||||
| translate}}
|
||||
<clr-dg-pagination #pagination [(clrDgPage)]="currentPage" [clrDgPageSize]="pageSize" [clrDgTotalItems]="totalCount"></clr-dg-pagination>
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
export const LOG_STYLES: string = `
|
||||
.h2-log-override {
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
|
||||
.action-head-pos {
|
||||
padding-right: 18px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.refresh-btn:hover {
|
||||
color: #007CBB;
|
||||
}
|
||||
|
||||
.custom-lines-button {
|
||||
padding: 0px !important;
|
||||
min-width: 25px !important;
|
||||
}
|
||||
|
||||
.lines-button-toggole {
|
||||
font-size: 16px;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.log-select {
|
||||
width: 130px;
|
||||
display: inline-block;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
.item-divider {
|
||||
height: 24px;
|
||||
display: inline-block;
|
||||
width: 1px;
|
||||
background-color: #ccc;
|
||||
opacity: 0.55;
|
||||
margin-left: 12px;
|
||||
top: 8px;
|
||||
position: relative;
|
||||
}
|
||||
.rightPos {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
right: 35px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.filterTag{float:left;margin-top:8px;}
|
||||
`;
|
||||
</div>
|
55
src/ui_ng/lib/src/log/recent-log.component.scss
Normal file
55
src/ui_ng/lib/src/log/recent-log.component.scss
Normal file
@ -0,0 +1,55 @@
|
||||
.h2-log-override {
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
|
||||
.action-head-pos {
|
||||
padding-right: 18px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.refresh-btn:hover {
|
||||
color: #007CBB;
|
||||
}
|
||||
|
||||
.custom-lines-button {
|
||||
padding: 0px !important;
|
||||
min-width: 25px !important;
|
||||
}
|
||||
|
||||
.lines-button-toggole {
|
||||
font-size: 16px;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.log-select {
|
||||
width: 130px;
|
||||
display: inline-block;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
.item-divider {
|
||||
height: 24px;
|
||||
display: inline-block;
|
||||
width: 1px;
|
||||
background-color: #ccc;
|
||||
opacity: 0.55;
|
||||
margin-left: 12px;
|
||||
top: 8px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.rightPos {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
right: 35px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.filterTag {
|
||||
float: left;
|
||||
margin-top: 8px;
|
||||
}
|
@ -22,7 +22,6 @@ import {
|
||||
import { ErrorHandler } from '../error-handler/index';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { toPromise, CustomComparator } from '../utils';
|
||||
import { LOG_TEMPLATE, LOG_STYLES } from './recent-log.template';
|
||||
import {
|
||||
DEFAULT_PAGE_SIZE,
|
||||
calculatePage,
|
||||
@ -34,8 +33,8 @@ import { Comparator, State } from 'clarity-angular';
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-log',
|
||||
styles: [LOG_STYLES],
|
||||
template: LOG_TEMPLATE
|
||||
templateUrl: './recent-log.component.html',
|
||||
styleUrls: ['./recent-log.component.scss']
|
||||
})
|
||||
|
||||
export class RecentLogComponent implements OnInit {
|
||||
|
@ -1,9 +0,0 @@
|
||||
export const PROJECT_POLICY_CONFIG_STYLE = `#severity-blk div
|
||||
{
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.select {
|
||||
width: 120px;
|
||||
}
|
||||
`;
|
@ -1,4 +1,3 @@
|
||||
export const PROJECT_POLICY_CONFIG_TEMPLATE = `
|
||||
<form #projectPolicyForm="ngForm">
|
||||
<section class="form-block">
|
||||
<div class="form-group">
|
||||
@ -48,4 +47,4 @@ export const PROJECT_POLICY_CONFIG_TEMPLATE = `
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()" [disabled]="!isValid() || !hasChanges() || !hasProjectAdminRole">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<confirmation-dialog #cfgConfirmationDialog (confirmAction)="confirmCancel($event)"></confirmation-dialog>
|
||||
</section>
|
||||
</form>`;
|
||||
</form>
|
@ -0,0 +1,8 @@
|
||||
#severity-blk div
|
||||
{
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.select {
|
||||
width: 120px;
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
import { Component, Input, OnInit, ViewChild } from '@angular/core';
|
||||
|
||||
import { PROJECT_POLICY_CONFIG_TEMPLATE } from './project-policy-config.component.html';
|
||||
import { PROJECT_POLICY_CONFIG_STYLE } from './project-policy-config.component.css';
|
||||
import { toPromise, compareValue, clone } from '../utils';
|
||||
import { ProjectService } from '../service/project.service';
|
||||
import { ErrorHandler } from '../error-handler/error-handler';
|
||||
@ -42,8 +40,8 @@ export class ProjectPolicy {
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-project-policy-config',
|
||||
template: PROJECT_POLICY_CONFIG_TEMPLATE,
|
||||
styles: [PROJECT_POLICY_CONFIG_STYLE],
|
||||
templateUrl: './project-policy-config.component.html',
|
||||
styleUrls: ['./project-policy-config.component.scss']
|
||||
})
|
||||
export class ProjectPolicyConfigComponent implements OnInit {
|
||||
onGoing = false;
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
|
||||
|
||||
import { COPY_INPUT_HTML } from './copy-input.html';
|
||||
import { PUSH_IMAGE_STYLE } from './push-image.css';
|
||||
|
||||
export const enum CopyStatus {
|
||||
NORMAL, SUCCESS, ERROR
|
||||
@ -9,8 +7,8 @@ export const enum CopyStatus {
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-copy-input',
|
||||
styles: [PUSH_IMAGE_STYLE],
|
||||
template: COPY_INPUT_HTML,
|
||||
templateUrl: './copy-input.coponent.html',
|
||||
styleUrls: ['./push-image.scss'],
|
||||
|
||||
providers: []
|
||||
})
|
||||
|
@ -1,4 +1,3 @@
|
||||
export const COPY_INPUT_HTML = `
|
||||
<div>
|
||||
<div class="command-title" *ngIf="!iconMode">
|
||||
{{headerTitle}}
|
||||
@ -14,5 +13,4 @@ export const COPY_INPUT_HTML = `
|
||||
<clr-icon shape="copy" [class.is-success]="isCopied" [class.is-error]="hasCopyError" class="info-tips-icon" size="24" [ngxClipboard]="inputTarget1" (cbOnSuccess)="onSuccess($event)" (cbOnError)="onError($event)"></clr-icon>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
</div>
|
@ -1,4 +1,3 @@
|
||||
export const PUSH_IMAGE_HTML: string = `
|
||||
<div>
|
||||
<clr-dropdown>
|
||||
<button class="btn btn-link btn-font" clrDropdownToggle (click)="onclick()">
|
||||
@ -32,5 +31,4 @@ export const PUSH_IMAGE_HTML: string = `
|
||||
</div>
|
||||
</clr-dropdown-menu>
|
||||
</clr-dropdown>
|
||||
</div>
|
||||
`;
|
||||
</div>
|
@ -2,13 +2,11 @@ import { Component, Input, ViewChild } from '@angular/core';
|
||||
import { CopyInputComponent } from './copy-input.component';
|
||||
import { InlineAlertComponent } from '../inline-alert/inline-alert.component';
|
||||
|
||||
import { PUSH_IMAGE_STYLE } from './push-image.css';
|
||||
import { PUSH_IMAGE_HTML } from './push-image.html';
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-push-image-button',
|
||||
template: PUSH_IMAGE_HTML,
|
||||
styles: [PUSH_IMAGE_STYLE],
|
||||
templateUrl: './push-image.component.html',
|
||||
styleUrls: ['./push-image.scss'],
|
||||
|
||||
providers: []
|
||||
})
|
||||
|
@ -1,4 +1,3 @@
|
||||
export const PUSH_IMAGE_STYLE: string = `
|
||||
.commands-container {
|
||||
min-width: 360px;
|
||||
max-width: 720px;
|
||||
@ -45,5 +44,4 @@ export const PUSH_IMAGE_STYLE: string = `
|
||||
}
|
||||
.hide{
|
||||
display:none;
|
||||
}
|
||||
`;
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
export const REPLICATION_TEMPLATE: string = `
|
||||
<div class="row" style="position:relative;">
|
||||
<div>
|
||||
<div class="row flex-items-xs-between rightPos">
|
||||
@ -75,4 +74,4 @@ export const REPLICATION_TEMPLATE: string = `
|
||||
<job-log-viewer #replicationLogViewer></job-log-viewer>
|
||||
<hbr-create-edit-rule *ngIf="isSystemAdmin" [projectId]="projectId" [projectName]="projectName" (goToRegistry)="goRegistry()" (reload)="reloadRules($event)"></hbr-create-edit-rule>
|
||||
<confirmation-dialog #replicationConfirmDialog [batchInfors]="batchDelectionInfos" (confirmAction)="confirmReplication($event)"></confirmation-dialog>
|
||||
</div>`;
|
||||
</div>
|
@ -1,4 +1,3 @@
|
||||
export const REPLICATION_STYLE: string = `
|
||||
.refresh-btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
@ -28,4 +27,4 @@ export const REPLICATION_STYLE: string = `
|
||||
margin-top: 5px;
|
||||
z-index: 100;
|
||||
height: 32px;
|
||||
}`;
|
||||
}
|
@ -34,9 +34,6 @@ import {
|
||||
|
||||
import { Comparator } from 'clarity-angular';
|
||||
|
||||
import { REPLICATION_TEMPLATE } from './replication.component.html';
|
||||
import { REPLICATION_STYLE } from './replication.component.css';
|
||||
|
||||
import { JobLogViewerComponent } from '../job-log-viewer/index';
|
||||
import { State } from "clarity-angular";
|
||||
import {Observable} from "rxjs/Observable";
|
||||
@ -81,8 +78,8 @@ export class SearchOption {
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-replication',
|
||||
template: REPLICATION_TEMPLATE,
|
||||
styles: [REPLICATION_STYLE]
|
||||
templateUrl: './replication.component.html',
|
||||
styleUrls: ['./replication.component.scss']
|
||||
})
|
||||
export class ReplicationComponent implements OnInit, OnDestroy {
|
||||
|
||||
|
@ -20,7 +20,7 @@ import { GridViewComponent } from '../gridview/grid-view.component';
|
||||
@Component({
|
||||
selector: 'hbr-repository-gridview',
|
||||
templateUrl: './repository-gridview.component.html',
|
||||
styleUrls: ['./repository-gridview.component.css'],
|
||||
styleUrls: ['./repository-gridview.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class RepositoryGridviewComponent implements OnChanges, OnInit {
|
||||
|
@ -1,6 +0,0 @@
|
||||
import { Type } from '@angular/core';
|
||||
import { RepositoryListviewComponent } from './repository-listview.component';
|
||||
|
||||
export const REPOSITORY_LISTVIEW_DIRECTIVES: Type<any>[] = [
|
||||
RepositoryListviewComponent
|
||||
];
|
@ -1,8 +0,0 @@
|
||||
export const REPOSITORY_LISTVIEW_STYLE = `
|
||||
.rightPos{
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
right: 35px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
`;
|
@ -1,41 +0,0 @@
|
||||
export const REPOSITORY_LISTVIEW_TEMPLATE = `
|
||||
<div>
|
||||
<div class="row" style="position:relative;">
|
||||
<div>
|
||||
<div class="row flex-items-xs-right option-right rightPos">
|
||||
<div class="flex-xs-middle">
|
||||
<hbr-push-image-button style="display: inline-block;" [registryUrl]="registryUrl" [projectName]="projectName"></hbr-push-image-button>
|
||||
<hbr-filter [withDivider]="true" filterPlaceholder="{{'REPOSITORY.FILTER_FOR_REPOSITORIES' | translate}}" (filter)="doSearchRepoNames($event)" [currentValue]="lastFilteredRepoName"></hbr-filter>
|
||||
<span class="refresh-btn" (click)="refresh()"><clr-icon shape="refresh"></clr-icon></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<clr-datagrid (clrDgRefresh)="clrLoad($event)" [clrDgLoading]="loading" [(clrDgSelected)]="selectedRow" (clrDgSelectedChange)="selectedChange()">
|
||||
<clr-dg-action-bar>
|
||||
<button type="button" class="btn btn-sm btn-secondary" (click)="deleteRepos(selectedRow)" [disabled]="!(selectedRow.length && hasProjectAdminRole)"><clr-icon shape="times" size="16"></clr-icon> {{'REPOSITORY.DELETE' | translate}}</button>
|
||||
</clr-dg-action-bar>
|
||||
<clr-dg-column [clrDgField]="'name'">{{'REPOSITORY.NAME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgSortBy]="tagsCountComparator">{{'REPOSITORY.TAGS_COUNT' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgSortBy]="pullCountComparator">{{'REPOSITORY.PULL_COUNT' | translate}}</clr-dg-column>
|
||||
<clr-dg-placeholder>{{'REPOSITORY.PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
||||
<clr-dg-row *ngFor="let r of repositories" [clrDgItem]="r">
|
||||
<clr-dg-cell><a href="javascript:void(0)" (click)="watchRepoClickEvt(r)">{{r.name}}</a></clr-dg-cell>
|
||||
<clr-dg-cell>{{r.tags_count}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{r.pull_count}}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
<span *ngIf="showDBStatusWarning" class="db-status-warning">
|
||||
<clr-icon shape="warning" class="is-warning" size="24"></clr-icon>
|
||||
{{'CONFIG.SCANNING.DB_NOT_READY' | translate }}
|
||||
</span>
|
||||
<span *ngIf="pagination.totalItems">{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'REPOSITORY.OF' | translate}}</span>
|
||||
{{pagination.totalItems}} {{'REPOSITORY.ITEMS' | translate}}
|
||||
<clr-dg-pagination #pagination [(clrDgPage)]="currentPage" [clrDgPageSize]="pageSize" [clrDgTotalItems]="totalCount"></clr-dg-pagination>
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
</div>
|
||||
</div>
|
||||
<confirmation-dialog #confirmationDialog [batchInfors]="batchDelectionInfos" (confirmAction)="confirmDeletion($event)"></confirmation-dialog>
|
||||
</div>
|
||||
`;
|
@ -1,173 +0,0 @@
|
||||
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { DebugElement } from '@angular/core';
|
||||
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component';
|
||||
import { RepositoryListviewComponent } from './repository-listview.component';
|
||||
import { TagComponent } from '../tag/tag.component';
|
||||
import { FilterComponent } from '../filter/filter.component';
|
||||
|
||||
import { ErrorHandler } from '../error-handler/error-handler';
|
||||
import { Repository, RepositoryItem, Tag, SystemInfo } from '../service/interface';
|
||||
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
||||
import { RepositoryService, RepositoryDefaultService } from '../service/repository.service';
|
||||
import { TagService, TagDefaultService } from '../service/tag.service';
|
||||
import { SystemInfoService, SystemInfoDefaultService } from '../service/system-info.service';
|
||||
import { VULNERABILITY_DIRECTIVES } from '../vulnerability-scanning/index';
|
||||
import { PUSH_IMAGE_BUTTON_DIRECTIVES } from '../push-image/index';
|
||||
import { INLINE_ALERT_DIRECTIVES } from '../inline-alert/index';
|
||||
import { JobLogViewerComponent } from '../job-log-viewer/index';
|
||||
|
||||
import { click } from '../utils';
|
||||
import {LabelPieceComponent} from "../label-piece/label-piece.component";
|
||||
|
||||
describe('RepositoryComponentListView (inline template)', () => {
|
||||
|
||||
let compRepo: RepositoryListviewComponent;
|
||||
let fixtureRepo: ComponentFixture<RepositoryListviewComponent>;
|
||||
let repositoryService: RepositoryService;
|
||||
let tagService: TagService;
|
||||
let systemInfoService: SystemInfoService;
|
||||
|
||||
let spyRepos: jasmine.Spy;
|
||||
let spySystemInfo: jasmine.Spy;
|
||||
|
||||
let mockSystemInfo: SystemInfo = {
|
||||
"with_notary": true,
|
||||
"with_admiral": false,
|
||||
"admiral_endpoint": "NA",
|
||||
"auth_mode": "db_auth",
|
||||
"registry_url": "10.112.122.56",
|
||||
"project_creation_restriction": "everyone",
|
||||
"self_registration": true,
|
||||
"has_ca_root": false,
|
||||
"harbor_version": "v1.1.1-rc1-160-g565110d"
|
||||
};
|
||||
|
||||
let mockRepoData: RepositoryItem[] = [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "library/busybox",
|
||||
"project_id": 1,
|
||||
"description": "asdfsadf",
|
||||
"pull_count": 0,
|
||||
"star_count": 0,
|
||||
"tags_count": 1
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "library/nginx",
|
||||
"project_id": 1,
|
||||
"description": "asdf",
|
||||
"pull_count": 0,
|
||||
"star_count": 0,
|
||||
"tags_count": 1
|
||||
}
|
||||
];
|
||||
|
||||
let mockRepo: Repository = {
|
||||
metadata: {xTotalCount: 2},
|
||||
data: mockRepoData
|
||||
};
|
||||
|
||||
let mockTagData: Tag[] = [
|
||||
{
|
||||
"digest": "sha256:e5c82328a509aeb7c18c1d7fb36633dc638fcf433f651bdcda59c1cc04d3ee55",
|
||||
"name": "1.11.5",
|
||||
"size": "2049",
|
||||
"architecture": "amd64",
|
||||
"os": "linux",
|
||||
"docker_version": "1.12.3",
|
||||
"author": "NGINX Docker Maintainers \"docker-maint@nginx.com\"",
|
||||
"created": new Date("2016-11-08T22:41:15.912313785Z"),
|
||||
"signature": null,
|
||||
"labels": []
|
||||
}
|
||||
];
|
||||
|
||||
let config: IServiceConfig = {
|
||||
repositoryBaseEndpoint: '/api/repository/testing',
|
||||
systemInfoEndpoint: '/api/systeminfo/testing',
|
||||
targetBaseEndpoint: '/api/tag/testing'
|
||||
};
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
SharedModule,
|
||||
RouterTestingModule
|
||||
],
|
||||
declarations: [
|
||||
RepositoryListviewComponent,
|
||||
TagComponent,
|
||||
LabelPieceComponent,
|
||||
ConfirmationDialogComponent,
|
||||
FilterComponent,
|
||||
VULNERABILITY_DIRECTIVES,
|
||||
PUSH_IMAGE_BUTTON_DIRECTIVES,
|
||||
INLINE_ALERT_DIRECTIVES,
|
||||
JobLogViewerComponent
|
||||
],
|
||||
providers: [
|
||||
ErrorHandler,
|
||||
{ provide: SERVICE_CONFIG, useValue: config },
|
||||
{ provide: RepositoryService, useClass: RepositoryDefaultService },
|
||||
{ provide: TagService, useClass: TagDefaultService },
|
||||
{ provide: SystemInfoService, useClass: SystemInfoDefaultService }
|
||||
]
|
||||
});
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixtureRepo = TestBed.createComponent(RepositoryListviewComponent);
|
||||
compRepo = fixtureRepo.componentInstance;
|
||||
compRepo.projectId = 1;
|
||||
compRepo.hasProjectAdminRole = true;
|
||||
|
||||
repositoryService = fixtureRepo.debugElement.injector.get(RepositoryService);
|
||||
systemInfoService = fixtureRepo.debugElement.injector.get(SystemInfoService);
|
||||
|
||||
spyRepos = spyOn(repositoryService, 'getRepositories').and.returnValues(Promise.resolve(mockRepo));
|
||||
spySystemInfo = spyOn(systemInfoService, 'getSystemInfo').and.returnValues(Promise.resolve(mockSystemInfo));
|
||||
fixtureRepo.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(compRepo).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should load and render data', async(() => {
|
||||
fixtureRepo.detectChanges();
|
||||
|
||||
fixtureRepo.whenStable().then(() => {
|
||||
fixtureRepo.detectChanges();
|
||||
|
||||
let deRepo: DebugElement = fixtureRepo.debugElement.query(By.css('datagrid-cell'));
|
||||
expect(deRepo).toBeTruthy();
|
||||
let elRepo: HTMLElement = deRepo.nativeElement;
|
||||
expect(elRepo).toBeTruthy();
|
||||
expect(elRepo.textContent).toEqual('library/busybox');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should filter data by keyword', async(() => {
|
||||
fixtureRepo.detectChanges();
|
||||
|
||||
fixtureRepo.whenStable().then(() => {
|
||||
fixtureRepo.detectChanges();
|
||||
|
||||
compRepo.doSearchRepoNames('nginx');
|
||||
fixtureRepo.detectChanges();
|
||||
let de: DebugElement[] = fixtureRepo.debugElement.queryAll(By.css('datagrid-cell'));
|
||||
expect(de).toBeTruthy();
|
||||
expect(de.length).toEqual(1);
|
||||
let el: HTMLElement = de[0].nativeElement;
|
||||
expect(el).toBeTruthy();
|
||||
expect(el.textContent).toEqual('library/nginx');
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
@ -1,354 +0,0 @@
|
||||
import {
|
||||
Component,
|
||||
Input,
|
||||
Output,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
EventEmitter, OnChanges, SimpleChanges
|
||||
} from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Comparator } from 'clarity-angular';
|
||||
|
||||
import { REPOSITORY_LISTVIEW_TEMPLATE } from './repository-listview.component.html';
|
||||
import { REPOSITORY_LISTVIEW_STYLE } from './repository-listview.component.css';
|
||||
|
||||
import {
|
||||
Repository,
|
||||
SystemInfo,
|
||||
SystemInfoService,
|
||||
RepositoryService,
|
||||
RequestQueryParams,
|
||||
RepositoryItem,
|
||||
TagService
|
||||
} from '../service/index';
|
||||
import { ErrorHandler } from '../error-handler/error-handler';
|
||||
|
||||
import { toPromise, CustomComparator } from '../utils';
|
||||
|
||||
import { ConfirmationState, ConfirmationTargets, ConfirmationButtons } from '../shared/shared.const';
|
||||
|
||||
import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component';
|
||||
import { ConfirmationMessage } from '../confirmation-dialog/confirmation-message';
|
||||
import { ConfirmationAcknowledgement } from '../confirmation-dialog/confirmation-state-message';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import { Tag } from '../service/interface';
|
||||
|
||||
import { State } from "clarity-angular";
|
||||
import {
|
||||
DEFAULT_PAGE_SIZE,
|
||||
calculatePage,
|
||||
doFiltering,
|
||||
doSorting
|
||||
} from '../utils';
|
||||
import {BatchInfo, BathInfoChanges} from "../confirmation-dialog/confirmation-batch-message";
|
||||
import {Observable} from "rxjs/Observable";
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-repository-listview',
|
||||
template: REPOSITORY_LISTVIEW_TEMPLATE,
|
||||
styles: [REPOSITORY_LISTVIEW_STYLE],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class RepositoryListviewComponent implements OnChanges, OnInit {
|
||||
signedCon: {[key: string]: any | string[]} = {};
|
||||
@Input() projectId: number;
|
||||
@Input() projectName = 'unknown';
|
||||
@Input() urlPrefix: string;
|
||||
|
||||
@Input() hasSignedIn: boolean;
|
||||
@Input() hasProjectAdminRole: boolean;
|
||||
@Output() repoClickEvent = new EventEmitter<RepositoryItem>();
|
||||
|
||||
lastFilteredRepoName: string;
|
||||
repositories: RepositoryItem[];
|
||||
systemInfo: SystemInfo;
|
||||
selectedRow: RepositoryItem[] = [];
|
||||
|
||||
loading = true;
|
||||
|
||||
@ViewChild('confirmationDialog')
|
||||
confirmationDialog: ConfirmationDialogComponent;
|
||||
|
||||
batchDelectionInfos: BatchInfo[] = [];
|
||||
pullCountComparator: Comparator<RepositoryItem> = new CustomComparator<RepositoryItem>('pull_count', 'number');
|
||||
|
||||
tagsCountComparator: Comparator<RepositoryItem> = new CustomComparator<RepositoryItem>('tags_count', 'number');
|
||||
|
||||
pageSize: number = DEFAULT_PAGE_SIZE;
|
||||
currentPage = 1;
|
||||
totalCount = 0;
|
||||
currentState: State;
|
||||
|
||||
constructor(
|
||||
private errorHandler: ErrorHandler,
|
||||
private translateService: TranslateService,
|
||||
private repositoryService: RepositoryService,
|
||||
private systemInfoService: SystemInfoService,
|
||||
private tagService: TagService,
|
||||
private ref: ChangeDetectorRef,
|
||||
private router: Router) { }
|
||||
|
||||
public get registryUrl(): string {
|
||||
return this.systemInfo ? this.systemInfo.registry_url : '';
|
||||
}
|
||||
|
||||
public get withNotary(): boolean {
|
||||
return this.systemInfo ? this.systemInfo.with_notary : false;
|
||||
}
|
||||
|
||||
public get withClair(): boolean {
|
||||
return this.systemInfo ? this.systemInfo.with_clair : false;
|
||||
}
|
||||
|
||||
public get isClairDBReady(): boolean {
|
||||
return this.systemInfo &&
|
||||
this.systemInfo.clair_vulnerability_status &&
|
||||
this.systemInfo.clair_vulnerability_status.overall_last_update > 0;
|
||||
}
|
||||
|
||||
public get showDBStatusWarning(): boolean {
|
||||
return this.withClair && !this.isClairDBReady;
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes['projectId'] && changes['projectId'].currentValue) {
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
// Get system info for tag views
|
||||
toPromise<SystemInfo>(this.systemInfoService.getSystemInfo())
|
||||
.then(systemInfo => this.systemInfo = systemInfo)
|
||||
.catch(error => this.errorHandler.error(error));
|
||||
|
||||
this.lastFilteredRepoName = '';
|
||||
}
|
||||
|
||||
confirmDeletion(message: ConfirmationAcknowledgement) {
|
||||
if (message &&
|
||||
message.source === ConfirmationTargets.REPOSITORY &&
|
||||
message.state === ConfirmationState.CONFIRMED) {
|
||||
|
||||
let promiseLists: any[] = [];
|
||||
let repoNames: string[] = message.data.split(',');
|
||||
|
||||
repoNames.forEach(repoName => {
|
||||
promiseLists.push(this.delOperate(repoName));
|
||||
});
|
||||
|
||||
Promise.all(promiseLists).then((item) => {
|
||||
this.selectedRow = [];
|
||||
this.refresh();
|
||||
let st: State = this.getStateAfterDeletion();
|
||||
if (!st) {
|
||||
this.refresh();
|
||||
} else {
|
||||
this.clrLoad(st);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
delOperate(repoName: string) {
|
||||
let findedList = this.batchDelectionInfos.find(data => data.name === repoName);
|
||||
if (this.signedCon[repoName].length !== 0) {
|
||||
Observable.forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
||||
this.translateService.get('REPOSITORY.DELETION_TITLE_REPO_SIGNED')).subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res[0], false, true, res[1]);
|
||||
});
|
||||
} else {
|
||||
return toPromise<number>(this.repositoryService
|
||||
.deleteRepository(repoName))
|
||||
.then(
|
||||
response => {
|
||||
this.translateService.get('BATCH.DELETED_SUCCESS').subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res);
|
||||
});
|
||||
}).catch(error => {
|
||||
if (error.status === "412") {
|
||||
Observable.forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
||||
this.translateService.get('REPOSITORY.TAGS_SIGNED')).subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res[0], false, true, res[1]);
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (error.status === 503) {
|
||||
Observable.forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
||||
this.translateService.get('REPOSITORY.TAGS_NO_DELETE')).subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res[0], false, true, res[1]);
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.translateService.get('BATCH.DELETED_FAILURE').subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res, false, true);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
doSearchRepoNames(repoName: string) {
|
||||
this.lastFilteredRepoName = repoName;
|
||||
this.currentPage = 1;
|
||||
|
||||
let st: State = this.currentState;
|
||||
if (!st) {
|
||||
st = { page: {} };
|
||||
}
|
||||
st.page.size = this.pageSize;
|
||||
st.page.from = 0;
|
||||
st.page.to = this.pageSize - 1;
|
||||
this.clrLoad(st);
|
||||
}
|
||||
|
||||
saveSignatures(event: {[key: string]: string[]}): void {
|
||||
Object.assign(this.signedCon, event);
|
||||
}
|
||||
|
||||
deleteRepos(repoLists: RepositoryItem[]) {
|
||||
if (repoLists && repoLists.length) {
|
||||
let repoNames: string[] = [];
|
||||
this.batchDelectionInfos = [];
|
||||
let repArr: any[] = [];
|
||||
|
||||
repoLists.forEach(repo => {
|
||||
repoNames.push(repo.name);
|
||||
let initBatchMessage = new BatchInfo();
|
||||
initBatchMessage.name = repo.name;
|
||||
this.batchDelectionInfos.push(initBatchMessage);
|
||||
|
||||
if (!this.signedCon[repo.name]) {
|
||||
repArr.push(this.getTagInfo(repo.name));
|
||||
}
|
||||
});
|
||||
|
||||
Promise.all(repArr).then(() => {
|
||||
this.confirmationDialogSet('REPOSITORY.DELETION_TITLE_REPO', '', repoNames.join(','), 'REPOSITORY.DELETION_SUMMARY_REPO', ConfirmationButtons.DELETE_CANCEL);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getTagInfo(repoName: string): Promise<void> {
|
||||
// this.signedNameArr = [];
|
||||
this.signedCon[repoName] = [];
|
||||
return toPromise<Tag[]>(this.tagService
|
||||
.getTags(repoName))
|
||||
.then(items => {
|
||||
items.forEach((t: Tag) => {
|
||||
if (t.signature !== null) {
|
||||
this.signedCon[repoName].push(t.name);
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(error => this.errorHandler.error(error));
|
||||
}
|
||||
|
||||
signedDataSet(repoName: string): void {
|
||||
let signature = '';
|
||||
if (this.signedCon[repoName].length === 0) {
|
||||
this.confirmationDialogSet('REPOSITORY.DELETION_TITLE_REPO', signature, repoName, 'REPOSITORY.DELETION_SUMMARY_REPO', ConfirmationButtons.DELETE_CANCEL);
|
||||
return;
|
||||
}
|
||||
signature = this.signedCon[repoName].join(',');
|
||||
this.confirmationDialogSet('REPOSITORY.DELETION_TITLE_REPO_SIGNED', signature, repoName, 'REPOSITORY.DELETION_SUMMARY_REPO_SIGNED', ConfirmationButtons.CLOSE);
|
||||
}
|
||||
|
||||
confirmationDialogSet(summaryTitle: string, signature: string, repoName: string, summaryKey: string, button: ConfirmationButtons): void {
|
||||
this.translateService.get(summaryKey,
|
||||
{
|
||||
repoName: repoName,
|
||||
signedImages: signature,
|
||||
})
|
||||
.subscribe((res: string) => {
|
||||
summaryKey = res;
|
||||
let message = new ConfirmationMessage(
|
||||
summaryTitle,
|
||||
summaryKey,
|
||||
repoName,
|
||||
repoName,
|
||||
ConfirmationTargets.REPOSITORY,
|
||||
button);
|
||||
this.confirmationDialog.open(message);
|
||||
|
||||
let hnd = setInterval(() => this.ref.markForCheck(), 100);
|
||||
setTimeout(() => clearInterval(hnd), 5000);
|
||||
});
|
||||
}
|
||||
|
||||
selectedChange(): void {
|
||||
let hnd = setInterval(() => this.ref.markForCheck(), 100);
|
||||
setTimeout(() => clearInterval(hnd), 2000);
|
||||
}
|
||||
refresh() {
|
||||
this.doSearchRepoNames('');
|
||||
}
|
||||
|
||||
clrLoad(state: State): void {
|
||||
this.selectedRow = [];
|
||||
// Keep it for future filtering and sorting
|
||||
this.currentState = state;
|
||||
|
||||
let pageNumber: number = calculatePage(state);
|
||||
if (pageNumber <= 0) { pageNumber = 1; }
|
||||
|
||||
// Pagination
|
||||
let params: RequestQueryParams = new RequestQueryParams();
|
||||
params.set("page", '' + pageNumber);
|
||||
params.set("page_size", '' + this.pageSize);
|
||||
|
||||
this.loading = true;
|
||||
|
||||
toPromise<Repository>(this.repositoryService.getRepositories(
|
||||
this.projectId,
|
||||
this.lastFilteredRepoName,
|
||||
params))
|
||||
.then((repo: Repository) => {
|
||||
this.totalCount = repo.metadata.xTotalCount;
|
||||
this.repositories = repo.data;
|
||||
|
||||
this.signedCon = {};
|
||||
// Do filtering and sorting
|
||||
this.repositories = doFiltering<RepositoryItem>(this.repositories, state);
|
||||
this.repositories = doSorting<RepositoryItem>(this.repositories, state);
|
||||
|
||||
this.loading = false;
|
||||
})
|
||||
.catch(error => {
|
||||
this.loading = false;
|
||||
this.errorHandler.error(error);
|
||||
});
|
||||
|
||||
// Force refresh view
|
||||
let hnd = setInterval(() => this.ref.markForCheck(), 100);
|
||||
setTimeout(() => clearInterval(hnd), 5000);
|
||||
}
|
||||
|
||||
getStateAfterDeletion(): State {
|
||||
let total: number = this.totalCount - 1;
|
||||
if (total <= 0) { return null; }
|
||||
|
||||
let totalPages: number = Math.ceil(total / this.pageSize);
|
||||
let targetPageNumber: number = this.currentPage;
|
||||
|
||||
if (this.currentPage > totalPages) {
|
||||
targetPageNumber = totalPages; // Should == currentPage -1
|
||||
}
|
||||
|
||||
let st: State = this.currentState;
|
||||
if (!st) {
|
||||
st = { page: {} };
|
||||
}
|
||||
st.page.size = this.pageSize;
|
||||
st.page.from = (targetPageNumber - 1) * this.pageSize;
|
||||
st.page.to = targetPageNumber * this.pageSize - 1;
|
||||
|
||||
return st;
|
||||
}
|
||||
|
||||
watchRepoClickEvt(repo: RepositoryItem) {
|
||||
this.repoClickEvent.emit(repo);
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
import { Type } from '@angular/core';
|
||||
import { RepositoryStackviewComponent } from './repository-stackview.component';
|
||||
|
||||
export const REPOSITORY_STACKVIEW_DIRECTIVES: Type<any>[] = [
|
||||
RepositoryStackviewComponent
|
||||
];
|
@ -1,47 +0,0 @@
|
||||
export const REPOSITORY_STACKVIEW_STYLES: string = `
|
||||
.option-right {
|
||||
padding-right: 16px;
|
||||
}
|
||||
.sub-grid-custom {
|
||||
left: 40px;
|
||||
}
|
||||
.refresh-btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
.refresh-btn:hover {
|
||||
color: #007CBB;
|
||||
}
|
||||
|
||||
:host >>> .datagrid .datagrid-body {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
:host >>> .datagrid .datagrid-foot {
|
||||
border-top: 1px solid #ccc;
|
||||
}
|
||||
|
||||
:host >>> .datagrid .datagrid-body .datagrid-row {
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
:host >>> .datagrid-body .datagrid-row .datagrid-row-master{
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
:host >>> .datagrid .datagrid-placeholder-container {
|
||||
display: none;
|
||||
}
|
||||
:host >>> .datagrid-overlay-wrapper{margin-top:24px;}
|
||||
|
||||
.db-status-warning {
|
||||
position: absolute;
|
||||
left: 24px;
|
||||
display: inline-block;
|
||||
}
|
||||
.rightPos{
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
right: 35px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
`;
|
@ -1,42 +0,0 @@
|
||||
export const REPOSITORY_STACKVIEW_TEMPLATE: string = `
|
||||
<div>
|
||||
<div class="row" style="margin-top: 20px; position: relative;">
|
||||
<div>
|
||||
<div class="row flex-items-xs-right rightPos">
|
||||
<div class="flex-xs-middle">
|
||||
<hbr-push-image-button style="display: inline-block;" [registryUrl]="registryUrl" [projectName]="projectName"></hbr-push-image-button>
|
||||
<hbr-filter [withDivider]="true" filterPlaceholder="{{'REPOSITORY.FILTER_FOR_REPOSITORIES' | translate}}" (filter)="doSearchRepoNames($event)" [currentValue]="lastFilteredRepoName"></hbr-filter>
|
||||
<span class="refresh-btn" (click)="refresh()"><clr-icon shape="refresh"></clr-icon></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<clr-datagrid (clrDgRefresh)="clrLoad($event)" [clrDgLoading]="loading">
|
||||
<clr-dg-column [clrDgField]="'name'">{{'REPOSITORY.NAME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgSortBy]="tagsCountComparator">{{'REPOSITORY.TAGS_COUNT' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgSortBy]="pullCountComparator">{{'REPOSITORY.PULL_COUNT' | translate}}</clr-dg-column>
|
||||
<clr-dg-placeholder>{{'REPOSITORY.PLACEHOLDER' | translate}}</clr-dg-placeholder>
|
||||
<clr-dg-row *ngFor="let r of repositories">
|
||||
<clr-dg-action-overflow [hidden]="!hasProjectAdminRole">
|
||||
<button class="action-item" (click)="deleteRepo(r.name)">{{'REPOSITORY.DELETE' | translate}}</button>
|
||||
</clr-dg-action-overflow>
|
||||
<clr-dg-cell>{{r.name}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{r.tags_count}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{r.pull_count}}</clr-dg-cell>
|
||||
<hbr-tag *clrIfExpanded ngProjectAs="clr-dg-row-detail" (tagClickEvent)="watchTagClickEvt($event)" (signatureOutput)="saveSignatures($event)" class="sub-grid-custom" [repoName]="r.name" [registryUrl]="registryUrl" [withNotary]="withNotary" [withClair]="withClair" [hasSignedIn]="hasSignedIn" [hasProjectAdminRole]="hasProjectAdminRole" [projectId]="projectId" [isEmbedded]="true"></hbr-tag>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
<span *ngIf="showDBStatusWarning" class="db-status-warning">
|
||||
<clr-icon shape="warning" class="is-warning" size="24"></clr-icon>
|
||||
{{'CONFIG.SCANNING.DB_NOT_READY' | translate }}
|
||||
</span>
|
||||
<span *ngIf="pagination.totalItems">{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'REPOSITORY.OF' | translate}}</span>
|
||||
{{pagination.totalItems}} {{'REPOSITORY.ITEMS' | translate}}
|
||||
<clr-dg-pagination #pagination [(clrDgPage)]="currentPage" [clrDgPageSize]="pageSize" [clrDgTotalItems]="totalCount"></clr-dg-pagination>
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
</div>
|
||||
</div>
|
||||
<confirmation-dialog #confirmationDialog (confirmAction)="confirmDeletion($event)"></confirmation-dialog>
|
||||
</div>
|
||||
`;
|
@ -1,224 +0,0 @@
|
||||
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { DebugElement } from '@angular/core';
|
||||
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component';
|
||||
import { RepositoryStackviewComponent } from './repository-stackview.component';
|
||||
import { TagComponent } from '../tag/tag.component';
|
||||
import { FilterComponent } from '../filter/filter.component';
|
||||
|
||||
import { ErrorHandler } from '../error-handler/error-handler';
|
||||
import {Repository, RepositoryItem, Tag, SystemInfo, Label} from '../service/interface';
|
||||
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
||||
import { RepositoryService, RepositoryDefaultService } from '../service/repository.service';
|
||||
import { TagService, TagDefaultService } from '../service/tag.service';
|
||||
import { SystemInfoService, SystemInfoDefaultService } from '../service/system-info.service';
|
||||
import { VULNERABILITY_DIRECTIVES } from '../vulnerability-scanning/index';
|
||||
import { PUSH_IMAGE_BUTTON_DIRECTIVES } from '../push-image/index';
|
||||
import { INLINE_ALERT_DIRECTIVES } from '../inline-alert/index';
|
||||
import { JobLogViewerComponent } from '../job-log-viewer/index';
|
||||
|
||||
import { click } from '../utils';
|
||||
import {LabelPieceComponent} from "../label-piece/label-piece.component";
|
||||
import {LabelDefaultService, LabelService} from "../service/label.service";
|
||||
|
||||
describe('RepositoryComponentStackview (inline template)', () => {
|
||||
|
||||
let compRepo: RepositoryStackviewComponent;
|
||||
let fixtureRepo: ComponentFixture<RepositoryStackviewComponent>;
|
||||
let repositoryService: RepositoryService;
|
||||
let tagService: TagService;
|
||||
let labelService: LabelService;
|
||||
let systemInfoService: SystemInfoService;
|
||||
|
||||
let spyRepos: jasmine.Spy;
|
||||
let spyTags: jasmine.Spy;
|
||||
let spyLabels: jasmine.Spy;
|
||||
let spySystemInfo: jasmine.Spy;
|
||||
|
||||
let mockSystemInfo: SystemInfo = {
|
||||
"with_notary": true,
|
||||
"with_admiral": false,
|
||||
"admiral_endpoint": "NA",
|
||||
"auth_mode": "db_auth",
|
||||
"registry_url": "10.112.122.56",
|
||||
"project_creation_restriction": "everyone",
|
||||
"self_registration": true,
|
||||
"has_ca_root": false,
|
||||
"harbor_version": "v1.1.1-rc1-160-g565110d"
|
||||
};
|
||||
|
||||
let mockRepoData: RepositoryItem[] = [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "library/busybox",
|
||||
"project_id": 1,
|
||||
"description": "",
|
||||
"pull_count": 0,
|
||||
"star_count": 0,
|
||||
"tags_count": 1
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "library/nginx",
|
||||
"project_id": 1,
|
||||
"description": "",
|
||||
"pull_count": 0,
|
||||
"star_count": 0,
|
||||
"tags_count": 1
|
||||
}
|
||||
];
|
||||
|
||||
let mockRepo: Repository = {
|
||||
metadata: {xTotalCount: 2},
|
||||
data: mockRepoData
|
||||
};
|
||||
|
||||
let mockTagData: Tag[] = [
|
||||
{
|
||||
"digest": "sha256:e5c82328a509aeb7c18c1d7fb36633dc638fcf433f651bdcda59c1cc04d3ee55",
|
||||
"name": "1.11.5",
|
||||
"size": "2049",
|
||||
"architecture": "amd64",
|
||||
"os": "linux",
|
||||
"docker_version": "1.12.3",
|
||||
"author": "NGINX Docker Maintainers \"docker-maint@nginx.com\"",
|
||||
"created": new Date("2016-11-08T22:41:15.912313785Z"),
|
||||
"signature": null,
|
||||
"labels": []
|
||||
}
|
||||
];
|
||||
|
||||
let mockLabels: Label[] = [
|
||||
{
|
||||
color: "#9b0d54",
|
||||
creation_time: "",
|
||||
description: "",
|
||||
id: 1,
|
||||
name: "label0-g",
|
||||
project_id: 0,
|
||||
scope: "g",
|
||||
update_time: "",
|
||||
},
|
||||
{
|
||||
color: "#9b0d54",
|
||||
creation_time: "",
|
||||
description: "",
|
||||
id: 2,
|
||||
name: "label1-g",
|
||||
project_id: 0,
|
||||
scope: "g",
|
||||
update_time: "",
|
||||
}
|
||||
];
|
||||
|
||||
let config: IServiceConfig = {
|
||||
repositoryBaseEndpoint: '/api/repository/testing',
|
||||
systemInfoEndpoint: '/api/systeminfo/testing',
|
||||
targetBaseEndpoint: '/api/tag/testing'
|
||||
};
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
SharedModule
|
||||
],
|
||||
declarations: [
|
||||
RepositoryStackviewComponent,
|
||||
TagComponent,
|
||||
LabelPieceComponent,
|
||||
ConfirmationDialogComponent,
|
||||
FilterComponent,
|
||||
VULNERABILITY_DIRECTIVES,
|
||||
PUSH_IMAGE_BUTTON_DIRECTIVES,
|
||||
INLINE_ALERT_DIRECTIVES,
|
||||
JobLogViewerComponent
|
||||
],
|
||||
providers: [
|
||||
ErrorHandler,
|
||||
{ provide: SERVICE_CONFIG, useValue: config },
|
||||
{ provide: RepositoryService, useClass: RepositoryDefaultService },
|
||||
{ provide: TagService, useClass: TagDefaultService },
|
||||
{ provide: SystemInfoService, useClass: SystemInfoDefaultService },
|
||||
{provide: LabelService, useClass: LabelDefaultService}
|
||||
]
|
||||
});
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixtureRepo = TestBed.createComponent(RepositoryStackviewComponent);
|
||||
compRepo = fixtureRepo.componentInstance;
|
||||
compRepo.projectId = 1;
|
||||
compRepo.hasProjectAdminRole = true;
|
||||
|
||||
repositoryService = fixtureRepo.debugElement.injector.get(RepositoryService);
|
||||
systemInfoService = fixtureRepo.debugElement.injector.get(SystemInfoService);
|
||||
|
||||
spyRepos = spyOn(repositoryService, 'getRepositories').and.returnValues(Promise.resolve(mockRepo));
|
||||
spySystemInfo = spyOn(systemInfoService, 'getSystemInfo').and.returnValues(Promise.resolve(mockSystemInfo));
|
||||
|
||||
labelService = fixtureRepo.debugElement.injector.get(LabelService);
|
||||
|
||||
spyLabels = spyOn(labelService, 'getLabels').and.returnValues(Promise.resolve(mockLabels));
|
||||
|
||||
fixtureRepo.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(compRepo).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should load and render data', async(() => {
|
||||
fixtureRepo.detectChanges();
|
||||
|
||||
fixtureRepo.whenStable().then(() => {
|
||||
fixtureRepo.detectChanges();
|
||||
|
||||
let deRepo: DebugElement = fixtureRepo.debugElement.query(By.css('datagrid-cell'));
|
||||
expect(deRepo).toBeTruthy();
|
||||
let elRepo: HTMLElement = deRepo.nativeElement;
|
||||
expect(elRepo).toBeTruthy();
|
||||
expect(elRepo.textContent).toEqual('library/busybox');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should filter data by keyword', async(() => {
|
||||
fixtureRepo.detectChanges();
|
||||
|
||||
fixtureRepo.whenStable().then(() => {
|
||||
fixtureRepo.detectChanges();
|
||||
|
||||
compRepo.doSearchRepoNames('nginx');
|
||||
fixtureRepo.detectChanges();
|
||||
let de: DebugElement[] = fixtureRepo.debugElement.queryAll(By.css('datagrid-cell'));
|
||||
expect(de).toBeTruthy();
|
||||
expect(de.length).toEqual(1);
|
||||
let el: HTMLElement = de[0].nativeElement;
|
||||
expect(el).toBeTruthy();
|
||||
expect(el.textContent).toEqual('library/nginx');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display embedded tag view when click >', async(() => {
|
||||
fixtureRepo.detectChanges();
|
||||
|
||||
fixtureRepo.whenStable().then(() => {
|
||||
fixtureRepo.detectChanges();
|
||||
|
||||
let el: HTMLElement = fixtureRepo.nativeElement.querySelector('.datagrid-expandable-caret');
|
||||
expect(el).toBeTruthy();
|
||||
let button: HTMLButtonElement = el.querySelector('button');
|
||||
expect(button).toBeTruthy();
|
||||
click(button);
|
||||
|
||||
fixtureRepo.detectChanges();
|
||||
let el2: HTMLElement = fixtureRepo.nativeElement.querySelector('.datagrid-row-detail');
|
||||
expect(el2).toBeTruthy();
|
||||
let el3: Element = el2.querySelector(".datagrid-cell");
|
||||
expect(el3).toBeTruthy();
|
||||
expect(el3.textContent).toEqual('1.11.5');
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
@ -1,298 +0,0 @@
|
||||
import {
|
||||
Component,
|
||||
Input,
|
||||
Output,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
EventEmitter, OnChanges, SimpleChanges
|
||||
} from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Comparator } from 'clarity-angular';
|
||||
|
||||
import { REPOSITORY_STACKVIEW_TEMPLATE } from './repository-stackview.component.html';
|
||||
import { REPOSITORY_STACKVIEW_STYLES } from './repository-stackview.component.css';
|
||||
|
||||
import {
|
||||
Repository,
|
||||
SystemInfo,
|
||||
SystemInfoService,
|
||||
RepositoryService,
|
||||
RequestQueryParams,
|
||||
RepositoryItem
|
||||
} from '../service/index';
|
||||
import { ErrorHandler } from '../error-handler/error-handler';
|
||||
|
||||
import { toPromise, CustomComparator } from '../utils';
|
||||
|
||||
import { ConfirmationState, ConfirmationTargets, ConfirmationButtons } from '../shared/shared.const';
|
||||
|
||||
import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component';
|
||||
import { ConfirmationMessage } from '../confirmation-dialog/confirmation-message';
|
||||
import { ConfirmationAcknowledgement } from '../confirmation-dialog/confirmation-state-message';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import { Tag, TagClickEvent } from '../service/interface';
|
||||
|
||||
import { State } from "clarity-angular";
|
||||
import {
|
||||
DEFAULT_PAGE_SIZE,
|
||||
calculatePage,
|
||||
doFiltering,
|
||||
doSorting
|
||||
} from '../utils';
|
||||
import {TagService} from '../service/index';
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-repository-stackview',
|
||||
template: REPOSITORY_STACKVIEW_TEMPLATE,
|
||||
styles: [REPOSITORY_STACKVIEW_STYLES],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class RepositoryStackviewComponent implements OnChanges, OnInit {
|
||||
signedCon: {[key: string]: any | string[]} = {};
|
||||
|
||||
@Input() projectId: number;
|
||||
@Input() projectName: string = "unknown";
|
||||
|
||||
@Input() hasSignedIn: boolean;
|
||||
@Input() hasProjectAdminRole: boolean;
|
||||
@Output() tagClickEvent = new EventEmitter<TagClickEvent>();
|
||||
|
||||
lastFilteredRepoName: string;
|
||||
repositories: RepositoryItem[];
|
||||
systemInfo: SystemInfo;
|
||||
|
||||
loading: boolean = true;
|
||||
|
||||
@ViewChild('confirmationDialog')
|
||||
confirmationDialog: ConfirmationDialogComponent;
|
||||
|
||||
pullCountComparator: Comparator<RepositoryItem> = new CustomComparator<RepositoryItem>('pull_count', 'number');
|
||||
|
||||
tagsCountComparator: Comparator<RepositoryItem> = new CustomComparator<RepositoryItem>('tags_count', 'number');
|
||||
|
||||
pageSize: number = DEFAULT_PAGE_SIZE;
|
||||
currentPage: number = 1;
|
||||
totalCount: number = 0;
|
||||
currentState: State;
|
||||
|
||||
constructor(
|
||||
private errorHandler: ErrorHandler,
|
||||
private translateService: TranslateService,
|
||||
private repositoryService: RepositoryService,
|
||||
private systemInfoService: SystemInfoService,
|
||||
private translate: TranslateService,
|
||||
private tagService: TagService,
|
||||
private ref: ChangeDetectorRef) { }
|
||||
|
||||
public get registryUrl(): string {
|
||||
return this.systemInfo ? this.systemInfo.registry_url : "";
|
||||
}
|
||||
|
||||
public get withNotary(): boolean {
|
||||
return this.systemInfo ? this.systemInfo.with_notary : false;
|
||||
}
|
||||
|
||||
public get withClair(): boolean {
|
||||
return this.systemInfo ? this.systemInfo.with_clair : false;
|
||||
}
|
||||
|
||||
public get isClairDBReady(): boolean {
|
||||
return this.systemInfo &&
|
||||
this.systemInfo.clair_vulnerability_status &&
|
||||
this.systemInfo.clair_vulnerability_status.overall_last_update > 0;
|
||||
}
|
||||
|
||||
public get showDBStatusWarning(): boolean {
|
||||
return this.withClair && !this.isClairDBReady;
|
||||
}
|
||||
|
||||
confirmDeletion(message: ConfirmationAcknowledgement) {
|
||||
if (message &&
|
||||
message.source === ConfirmationTargets.REPOSITORY &&
|
||||
message.state === ConfirmationState.CONFIRMED) {
|
||||
let repoName = message.data;
|
||||
toPromise<number>(this.repositoryService
|
||||
.deleteRepository(repoName))
|
||||
.then(
|
||||
response => {
|
||||
this.refresh();
|
||||
let st: State = this.getStateAfterDeletion();
|
||||
if (!st) {
|
||||
this.refresh();
|
||||
} else {
|
||||
this.clrLoad(st);
|
||||
}
|
||||
this.translateService.get('REPOSITORY.DELETED_REPO_SUCCESS')
|
||||
.subscribe(res => this.errorHandler.info(res));
|
||||
}).catch(error => {
|
||||
if (error.status === "412"){
|
||||
this.translateService.get('REPOSITORY.TAGS_SIGNED')
|
||||
.subscribe(res => this.errorHandler.info(res));
|
||||
return;
|
||||
}
|
||||
this.errorHandler.error(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes['projectId'] && changes['projectId'].currentValue) {
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
//Get system info for tag views
|
||||
toPromise<SystemInfo>(this.systemInfoService.getSystemInfo())
|
||||
.then(systemInfo => this.systemInfo = systemInfo)
|
||||
.catch(error => this.errorHandler.error(error));
|
||||
|
||||
this.lastFilteredRepoName = '';
|
||||
}
|
||||
|
||||
doSearchRepoNames(repoName: string) {
|
||||
this.lastFilteredRepoName = repoName;
|
||||
this.currentPage = 1;
|
||||
|
||||
let st: State = this.currentState;
|
||||
if (!st) {
|
||||
st = { page: {} };
|
||||
}
|
||||
st.page.size = this.pageSize;
|
||||
st.page.from = 0;
|
||||
st.page.to = this.pageSize - 1;
|
||||
this.clrLoad(st);
|
||||
}
|
||||
|
||||
saveSignatures(event: {[key: string]: string[]}): void {
|
||||
Object.assign(this.signedCon, event);
|
||||
}
|
||||
|
||||
deleteRepo(repoName: string) {
|
||||
if (this.signedCon[repoName]) {
|
||||
this.signedDataSet(repoName);
|
||||
} else {
|
||||
this.getTagInfo(repoName).then(() => {
|
||||
this.signedDataSet(repoName);
|
||||
});
|
||||
}
|
||||
}
|
||||
getTagInfo(repoName: string): Promise<void> {
|
||||
// this.signedNameArr = [];
|
||||
this.signedCon[repoName] = [];
|
||||
return toPromise<Tag[]>(this.tagService
|
||||
.getTags(repoName))
|
||||
.then(items => {
|
||||
items.forEach((t: Tag) => {
|
||||
if (t.signature !== null) {
|
||||
this.signedCon[repoName].push(t.name);
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(error => this.errorHandler.error(error));
|
||||
}
|
||||
|
||||
signedDataSet(repoName: string): void {
|
||||
let signature: string = '';
|
||||
if (this.signedCon[repoName].length === 0) {
|
||||
this.confirmationDialogSet('REPOSITORY.DELETION_TITLE_REPO', signature, repoName, 'REPOSITORY.DELETION_SUMMARY_REPO', ConfirmationButtons.DELETE_CANCEL);
|
||||
return;
|
||||
}
|
||||
signature = this.signedCon[repoName].join(',');
|
||||
this.confirmationDialogSet('REPOSITORY.DELETION_TITLE_REPO_SIGNED', signature, repoName, 'REPOSITORY.DELETION_SUMMARY_REPO_SIGNED', ConfirmationButtons.CLOSE);
|
||||
}
|
||||
|
||||
confirmationDialogSet(summaryTitle: string, signature: string, repoName: string, summaryKey: string, button: ConfirmationButtons): void {
|
||||
this.translate.get(summaryKey,
|
||||
{
|
||||
repoName: repoName,
|
||||
signedImages: signature,
|
||||
})
|
||||
.subscribe((res: string) => {
|
||||
summaryKey = res;
|
||||
let message = new ConfirmationMessage(
|
||||
summaryTitle,
|
||||
summaryKey,
|
||||
repoName,
|
||||
repoName,
|
||||
ConfirmationTargets.REPOSITORY,
|
||||
button);
|
||||
this.confirmationDialog.open(message);
|
||||
|
||||
let hnd = setInterval(() => this.ref.markForCheck(), 100);
|
||||
setTimeout(() => clearInterval(hnd), 5000);
|
||||
});
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.doSearchRepoNames("");
|
||||
}
|
||||
|
||||
watchTagClickEvt(tagClickEvt: TagClickEvent): void {
|
||||
this.tagClickEvent.emit(tagClickEvt);
|
||||
}
|
||||
|
||||
clrLoad(state: State): void {
|
||||
//Keep it for future filtering and sorting
|
||||
this.currentState = state;
|
||||
|
||||
let pageNumber: number = calculatePage(state);
|
||||
if (pageNumber <= 0) { pageNumber = 1; }
|
||||
|
||||
//Pagination
|
||||
let params: RequestQueryParams = new RequestQueryParams();
|
||||
params.set("page", '' + pageNumber);
|
||||
params.set("page_size", '' + this.pageSize);
|
||||
|
||||
this.loading = true;
|
||||
|
||||
toPromise<Repository>(this.repositoryService.getRepositories(
|
||||
this.projectId,
|
||||
this.lastFilteredRepoName,
|
||||
params))
|
||||
.then((repo: Repository) => {
|
||||
this.totalCount = repo.metadata.xTotalCount;
|
||||
this.repositories = repo.data;
|
||||
|
||||
this.signedCon = {};
|
||||
//Do filtering and sorting
|
||||
this.repositories = doFiltering<RepositoryItem>(this.repositories, state);
|
||||
this.repositories = doSorting<RepositoryItem>(this.repositories, state);
|
||||
|
||||
this.loading = false;
|
||||
})
|
||||
.catch(error => {
|
||||
this.loading = false;
|
||||
this.errorHandler.error(error);
|
||||
});
|
||||
|
||||
//Force refresh view
|
||||
let hnd = setInterval(() => this.ref.markForCheck(), 100);
|
||||
setTimeout(() => clearInterval(hnd), 5000);
|
||||
}
|
||||
|
||||
getStateAfterDeletion(): State {
|
||||
let total: number = this.totalCount - 1;
|
||||
if (total <= 0) { return null; }
|
||||
|
||||
let totalPages: number = Math.ceil(total / this.pageSize);
|
||||
let targetPageNumber: number = this.currentPage;
|
||||
|
||||
if (this.currentPage > totalPages) {
|
||||
targetPageNumber = totalPages;//Should == currentPage -1
|
||||
}
|
||||
|
||||
let st: State = this.currentState;
|
||||
if (!st) {
|
||||
st = { page: {} };
|
||||
}
|
||||
st.page.size = this.pageSize;
|
||||
st.page.from = (targetPageNumber - 1) * this.pageSize;
|
||||
st.page.to = targetPageNumber * this.pageSize - 1;
|
||||
|
||||
return st;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
export const REPOSITORY_TEMPLATE = `
|
||||
<section class="overview-section">
|
||||
<div class="title-wrapper">
|
||||
<div class="title-block arrow-block" *ngIf="withAdmiral">
|
||||
@ -50,5 +49,4 @@ export const REPOSITORY_TEMPLATE = `
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
`;
|
||||
</section>
|
@ -1,4 +1,4 @@
|
||||
export const REPOSITORY_STYLE = `.option-right {
|
||||
.option-right {
|
||||
padding-right: 16px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
@ -56,5 +56,4 @@ export const REPOSITORY_STYLE = `.option-right {
|
||||
harbor-tag {
|
||||
position: relative;
|
||||
top: 24px;
|
||||
}
|
||||
`;
|
||||
}
|
@ -6,7 +6,8 @@ import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component';
|
||||
import { RepositoryComponent } from './repository.component';
|
||||
import { RepositoryListviewComponent } from '../repository-listview/repository-listview.component';
|
||||
import { RepositoryGridviewComponent } from '../repository-gridview/repository-gridview.component';
|
||||
import { GridViewComponent } from '../gridview/grid-view.component';
|
||||
import { FilterComponent } from '../filter/filter.component';
|
||||
import { TagComponent } from '../tag/tag.component';
|
||||
import { VULNERABILITY_DIRECTIVES } from '../vulnerability-scanning/index';
|
||||
@ -154,7 +155,8 @@ describe('RepositoryComponent (inline template)', () => {
|
||||
],
|
||||
declarations: [
|
||||
RepositoryComponent,
|
||||
RepositoryListviewComponent,
|
||||
GridViewComponent,
|
||||
RepositoryGridviewComponent,
|
||||
ConfirmationDialogComponent,
|
||||
FilterComponent,
|
||||
TagComponent,
|
||||
|
@ -23,9 +23,6 @@ import { ConfirmationState, ConfirmationTargets } from '../shared/shared.const';
|
||||
import { ConfirmationDialogComponent, ConfirmationMessage, ConfirmationAcknowledgement } from '../confirmation-dialog/index';
|
||||
import { toPromise } from '../utils';
|
||||
|
||||
import { REPOSITORY_TEMPLATE } from './repository.component.html';
|
||||
import { REPOSITORY_STYLE } from './repository.component.css';
|
||||
|
||||
const TabLinkContentMap: {[index: string]: string} = {
|
||||
'repo-info': 'info',
|
||||
'repo-image': 'image'
|
||||
@ -33,8 +30,8 @@ const TabLinkContentMap: {[index: string]: string} = {
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-repository',
|
||||
template: REPOSITORY_TEMPLATE,
|
||||
styles: [REPOSITORY_STYLE]
|
||||
templateUrl: './repository.component.html',
|
||||
styleUrls: ['./repository.component.scss']
|
||||
})
|
||||
export class RepositoryComponent implements OnInit {
|
||||
signedCon: {[key: string]: any | string[]} = {};
|
||||
|
@ -1,4 +1,3 @@
|
||||
export const TAG_DETAIL_HTML: string = `
|
||||
<div>
|
||||
<section class="overview-section">
|
||||
<div class="title-wrapper">
|
||||
@ -72,5 +71,4 @@ export const TAG_DETAIL_HTML: string = `
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
`;
|
||||
</div>
|
@ -1,4 +1,3 @@
|
||||
export const TAG_DETAIL_STYLES: string = `
|
||||
.overview-section {
|
||||
padding-bottom: 36px;
|
||||
}
|
||||
@ -124,6 +123,4 @@ margin-left:20px;}
|
||||
}
|
||||
.second-column div, .fourth-column div, .image-detail-value div{
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
`;
|
||||
}
|
@ -1,8 +1,5 @@
|
||||
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
|
||||
|
||||
import { TAG_DETAIL_STYLES } from './tag-detail.component.css';
|
||||
import { TAG_DETAIL_HTML } from './tag-detail.component.html';
|
||||
|
||||
import { TagService, Tag, VulnerabilitySeverity } from '../service/index';
|
||||
import { toPromise } from '../utils';
|
||||
import { ErrorHandler } from '../error-handler/index';
|
||||
@ -10,8 +7,8 @@ import {Label} from "../service/interface";
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-tag-detail',
|
||||
styles: [TAG_DETAIL_STYLES],
|
||||
template: TAG_DETAIL_HTML,
|
||||
templateUrl: './tag-detail.component.html',
|
||||
styleUrls: ['./tag-detail.component.scss'],
|
||||
|
||||
providers: []
|
||||
})
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user