mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-23 00:57:44 +01:00
Merge pull request #2507 from steven-zou/master
Improve components in UI library
This commit is contained in:
commit
78f8a09177
@ -52,10 +52,9 @@ If no parameters are passed to **'forRoot'**, the module will be initialized wit
|
||||
|
||||
* **Registry log view**
|
||||
|
||||
Use **withTitle** to set whether self-contained a header with title or not. Default is **false**, that means no header is existing.
|
||||
```
|
||||
//No @Input properties
|
||||
|
||||
<hbr-log></hbr-log>
|
||||
<hbr-log [withTitle]="..."></hbr-log>
|
||||
```
|
||||
|
||||
* **Replication Management View**
|
||||
@ -85,8 +84,18 @@ If **projectId** is set to the id of specified project, then only show the repli
|
||||
|
||||
**hasProjectAdminRole** is a user session related property to determined whether the current user has project administrator role. Some action menus might be disabled based on this property.
|
||||
|
||||
**tagClickEvent** is an @output event emitter for you to catch the tag click events.
|
||||
|
||||
```
|
||||
<hbr-repository-stackview [projectId]="..." [hasSignedIn]="..." [hasProjectAdminRole]="..."></hbr-repository-stackview>
|
||||
<hbr-repository-stackview [projectId]="..." [hasSignedIn]="..." [hasProjectAdminRole]="..." (tagClickEvent)="watchTagClickEvent($event)"></hbr-repository-stackview>
|
||||
|
||||
...
|
||||
|
||||
watchTagClickEvent(tag: Tag): void {
|
||||
//Process tag
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Configurations
|
||||
@ -96,7 +105,7 @@ All the related configurations are defined in the **HarborModuleConfig** interfa
|
||||
The base configuration for the module. Mainly used to define the relevant endpoints of services which are in charge of retrieving data from backend APIs. It's a 'OpaqueToken' and defined by 'IServiceConfig' interface. If **config** is not set, the default value will be used.
|
||||
```
|
||||
export const DefaultServiceConfig: IServiceConfig = {
|
||||
systemInfoEndpoint: "/api/system",
|
||||
systemInfoEndpoint: "/api/systeminfo",
|
||||
repositoryBaseEndpoint: "/api/repositories",
|
||||
logBaseEndpoint: "/api/logs",
|
||||
targetBaseEndpoint: "/api/targets",
|
||||
@ -126,6 +135,8 @@ HarborLibraryModule.forRoot({
|
||||
|
||||
```
|
||||
It supports partially overriding. For the items not overridden, default values will be adopted. The items contained in **config** are:
|
||||
* **systemInfoEndpoint:** The base endpoint of the service used to get the related system configurations. Default value is "/api/systeminfo".
|
||||
|
||||
* **repositoryBaseEndpoint:** The base endpoint of the service used to handle the repositories of registry and/or tags of repository. Default value is "/api/repositories".
|
||||
|
||||
* **logBaseEndpoint:** The base endpoint of the service used to handle the recent access logs. Default is "/api/logs".
|
||||
@ -578,32 +589,39 @@ HarborLibraryModule.forRoot({
|
||||
* **ScanningResultService:** Get the vulnerabilities scanning results for the specified tag.
|
||||
```
|
||||
@Injectable()
|
||||
/**
|
||||
* Get the vulnerabilities scanning results for the specified tag.
|
||||
*
|
||||
* @export
|
||||
* @abstract
|
||||
* @class ScanningResultService
|
||||
*/
|
||||
export class MyScanningResultService extends ScanningResultService {
|
||||
/**
|
||||
* Get the summary of vulnerability scanning result.
|
||||
*
|
||||
* @abstract
|
||||
* @param {string} tagId
|
||||
* @returns {(Observable<ScanningResultSummary> | Promise<ScanningResultSummary> | ScanningResultSummary)}
|
||||
* @returns {(Observable<VulnerabilitySummary> | Promise<VulnerabilitySummary> | VulnerabilitySummary)}
|
||||
*
|
||||
* @memberOf ScanningResultService
|
||||
*/
|
||||
getScanningResultSummary(tagId: string): Observable<ScanningResultSummary> | Promise<ScanningResultSummary> | ScanningResultSummary {
|
||||
...
|
||||
}
|
||||
getVulnerabilityScanningSummary(tagId: string): Observable<VulnerabilitySummary> | Promise<VulnerabilitySummary> | VulnerabilitySummary{
|
||||
...
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the detailed vulnerabilities scanning results.
|
||||
*
|
||||
* @abstract
|
||||
* @param {string} tagId
|
||||
* @returns {(Observable<ScanningDetailResult[]> | Promise<ScanningDetailResult[]> | ScanningDetailResult[])}
|
||||
* @returns {(Observable<VulnerabilityItem[]> | Promise<VulnerabilityItem[]> | VulnerabilityItem[])}
|
||||
*
|
||||
* @memberOf ScanningResultService
|
||||
*/
|
||||
getScanningResults(tagId: string): Observable<ScanningDetailResult[]> | Promise<ScanningDetailResult[]> | ScanningDetailResult[] {
|
||||
...
|
||||
}
|
||||
getVulnerabilityScanningResults(tagId: string): Observable<VulnerabilityItem[]> | Promise<VulnerabilityItem[]> | VulnerabilityItem[]{
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
...
|
||||
@ -612,4 +630,30 @@ HarborLibraryModule.forRoot({
|
||||
})
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
* **SystemInfoService:** Get related system configurations.
|
||||
```
|
||||
/**
|
||||
* Get System information about current backend server.
|
||||
* @abstract
|
||||
* @class
|
||||
*/
|
||||
export class MySystemInfoService extends SystemInfoService {
|
||||
/**
|
||||
* Get global system information.
|
||||
* @abstract
|
||||
* @returns
|
||||
*/
|
||||
getSystemInfo(): Observable<SystemInfo> | Promise<SystemInfo> | SystemInfo {
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
...
|
||||
HarborLibraryModule.forRoot({
|
||||
systemInfoService: { provide: SystemInfoService, useClass: MySystemInfoService }
|
||||
})
|
||||
...
|
||||
|
||||
```
|
@ -1,10 +1,15 @@
|
||||
export const ENDPOINT_STYLE: string = `
|
||||
.option-left {
|
||||
padding-left: 16px;
|
||||
margin-top: 24px;
|
||||
margin-top: -6px;
|
||||
}
|
||||
.option-right {
|
||||
padding-right: 16px;
|
||||
margin-top: 36px;
|
||||
}
|
||||
.refresh-btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
.refresh-btn:hover {
|
||||
color: #007CBB;
|
||||
}
|
||||
`;
|
@ -1,17 +1,17 @@
|
||||
export const ENDPOINT_TEMPLATE: string = `
|
||||
<confirmation-dialog #confirmationDialog (confirmAction)="confirmDeletion($event)"></confirmation-dialog>
|
||||
<div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="row flex-items-xs-between">
|
||||
<div class="row flex-items-xs-between" style="height: 24px;">
|
||||
<div class="flex-items-xs-middle option-left">
|
||||
<button class="btn btn-link" (click)="openModal()"><clr-icon shape="add"></clr-icon> {{'DESTINATION.ENDPOINT' | translate}}</button>
|
||||
<create-edit-endpoint (reload)="reload($event)"></create-edit-endpoint>
|
||||
</div>
|
||||
<div class="flex-items-xs-middle option-right">
|
||||
<hbr-filter filterPlaceholder='{{"REPLICATION.FILTER_TARGETS_PLACEHOLDER" | translate}}' (filter)="doSearchTargets($event)" [currentValue]="targetName"></hbr-filter>
|
||||
<a href="javascript:void(0)" (click)="refreshTargets()">
|
||||
<hbr-filter [withDivider]="true" filterPlaceholder='{{"REPLICATION.FILTER_TARGETS_PLACEHOLDER" | translate}}' (filter)="doSearchTargets($event)" [currentValue]="targetName"></hbr-filter>
|
||||
<span class="refresh-btn" (click)="refreshTargets()">
|
||||
<clr-icon shape="refresh"></clr-icon>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -20,6 +20,7 @@ export const ENDPOINT_TEMPLATE: string = `
|
||||
<clr-dg-column [clrDgField]="'name'">{{'DESTINATION.NAME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'endpoint'">{{'DESTINATION.URL' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgSortBy]="creationTimeComparator">{{'DESTINATION.CREATION_TIME' | translate}}</clr-dg-column>
|
||||
<clr-dg-placeholder>{{'DESTINATION.PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
||||
<clr-dg-row *clrDgItems="let t of targets" [clrDgItem]='t'>
|
||||
<clr-dg-action-overflow>
|
||||
<button class="action-item" (click)="editTarget(t)">{{'DESTINATION.TITLE_EDIT' | translate}}</button>
|
||||
@ -37,4 +38,6 @@ export const ENDPOINT_TEMPLATE: string = `
|
||||
</clr-datagrid>
|
||||
</div>
|
||||
</div>
|
||||
<confirmation-dialog #confirmationDialog (confirmAction)="confirmDeletion($event)"></confirmation-dialog>
|
||||
</div>
|
||||
`;
|
@ -31,6 +31,7 @@ export class FilterComponent implements OnInit {
|
||||
|
||||
placeHolder: string = "";
|
||||
filterTerms = new Subject<string>();
|
||||
isExpanded: boolean = false;
|
||||
|
||||
@Output("filter") private filterEvt = new EventEmitter<string>();
|
||||
|
||||
@ -39,6 +40,8 @@ export class FilterComponent implements OnInit {
|
||||
public set flPlaceholder(placeHolder: string) {
|
||||
this.placeHolder = placeHolder;
|
||||
}
|
||||
@Input() expandMode: boolean = false;
|
||||
@Input() withDivider: boolean = false;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.filterTerms
|
||||
@ -54,4 +57,16 @@ export class FilterComponent implements OnInit {
|
||||
//Send out filter terms
|
||||
this.filterTerms.next(this.currentValue.trim());
|
||||
}
|
||||
|
||||
onClick(): void {
|
||||
//Only enabled when expandMode is set to false
|
||||
if(this.expandMode){
|
||||
return;
|
||||
}
|
||||
this.isExpanded = !this.isExpanded;
|
||||
}
|
||||
|
||||
public get isShowSearchBox(): boolean {
|
||||
return this.expandMode || (!this.expandMode && this.isExpanded);
|
||||
}
|
||||
}
|
@ -4,8 +4,9 @@
|
||||
|
||||
export const FILTER_TEMPLATE: string = `
|
||||
<span>
|
||||
<clr-icon shape="filter" size="12" class="is-solid filter-icon"></clr-icon>
|
||||
<input type="text" style="padding-left: 15px;" (keyup)="valueChange()" placeholder="{{placeHolder}}" [(ngModel)]="currentValue"/>
|
||||
<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()" placeholder="{{placeHolder}}" [(ngModel)]="currentValue"/>
|
||||
<span class="filter-divider" *ngIf="withDivider"></span>
|
||||
</span>
|
||||
`;
|
||||
|
||||
@ -14,4 +15,25 @@ export const FILTER_STYLES: string = `
|
||||
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,6 +1,5 @@
|
||||
export const LIST_REPLICATION_RULE_TEMPLATE: string = `
|
||||
<confirmation-dialog #toggleConfirmDialog (confirmAction)="toggleConfirm($event)"></confirmation-dialog>
|
||||
<confirmation-dialog #deletionConfirmDialog (confirmAction)="deletionConfirm($event)"></confirmation-dialog>
|
||||
<div>
|
||||
<clr-datagrid [clrDgLoading]="loading">
|
||||
<clr-dg-column [clrDgField]="'name'">{{'REPLICATION.NAME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'project_name'" *ngIf="projectScope">{{'REPLICATION.PROJECT' | translate}}</clr-dg-column>
|
||||
@ -8,6 +7,7 @@ export const LIST_REPLICATION_RULE_TEMPLATE: string = `
|
||||
<clr-dg-column [clrDgField]="'target_name'">{{'REPLICATION.DESTINATION_NAME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgSortBy]="startTimeComparator">{{'REPLICATION.LAST_START_TIME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgSortBy]="enabledComparator">{{'REPLICATION.ACTIVATION' | translate}}</clr-dg-column>
|
||||
<clr-dg-placeholder>{{'REPLICATION.PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
||||
<clr-dg-row *clrDgItems="let p of changedRules" [clrDgItem]="p" (click)="selectRule(p)" [style.backgroundColor]="(!projectScope && withReplicationJob && selectedId === p.id) ? '#eee' : ''">
|
||||
<clr-dg-action-overflow>
|
||||
<button class="action-item" (click)="editRule(p)">{{'REPLICATION.EDIT_POLICY' | translate}}</button>
|
||||
@ -37,4 +37,8 @@ export const LIST_REPLICATION_RULE_TEMPLATE: string = `
|
||||
{{pagination.firstItem + 1}} - {{pagination.lastItem +1 }} {{'REPLICATION.OF' | translate}} {{pagination.totalItems }} {{'REPLICATION.ITEMS' | translate}}
|
||||
<clr-dg-pagination #pagination [clrDgPageSize]="5"></clr-dg-pagination>
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>`;
|
||||
</clr-datagrid>
|
||||
<confirmation-dialog #toggleConfirmDialog (confirmAction)="toggleConfirm($event)"></confirmation-dialog>
|
||||
<confirmation-dialog #deletionConfirmDialog (confirmAction)="deletionConfirm($event)"></confirmation-dialog>
|
||||
</div>
|
||||
`;
|
@ -3,6 +3,7 @@ export const LIST_REPOSITORY_TEMPLATE = `
|
||||
<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 *clrDgItems="let r of repositories" [clrDgItem]='r'>
|
||||
<clr-dg-action-overflow [hidden]="!hasProjectAdminRole">
|
||||
<button class="action-item" (click)="deleteRepo(r.name)">{{'REPOSITORY.DELETE' | translate}}</button>
|
||||
|
@ -11,7 +11,7 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import {
|
||||
AccessLogService,
|
||||
@ -23,6 +23,7 @@ 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 } from '../utils';
|
||||
|
||||
import { Comparator, State } from 'clarity-angular';
|
||||
|
||||
@ -37,11 +38,12 @@ export class RecentLogComponent implements OnInit {
|
||||
logsCache: AccessLog;
|
||||
loading: boolean = true;
|
||||
currentTerm: string;
|
||||
@Input() withTitle: boolean = false;
|
||||
|
||||
pageSize: number = 15;
|
||||
pageSize: number = DEFAULT_PAGE_SIZE;
|
||||
currentPage: number = 0;
|
||||
|
||||
opTimeComparator: Comparator<AccessLog> = new CustomComparator<AccessLog>('op_time', 'date');
|
||||
opTimeComparator: Comparator<AccessLogItem> = new CustomComparator<AccessLogItem>('op_time', 'date');
|
||||
|
||||
constructor(
|
||||
private logService: AccessLogService,
|
||||
|
@ -4,11 +4,11 @@
|
||||
|
||||
export const LOG_TEMPLATE: string = `
|
||||
<div>
|
||||
<h2 class="h2-log-override">{{'SIDE_NAV.LOGS' | translate}}</h2>
|
||||
<h2 class="h2-log-override" *ngIf="withTitle">{{'SIDE_NAV.LOGS' | translate}}</h2>
|
||||
<div class="row flex-items-xs-between flex-items-xs-bottom">
|
||||
<div></div>
|
||||
<div class="action-head-pos">
|
||||
<hbr-filter filterPlaceholder='{{"AUDIT_LOG.FILTER_PLACEHOLDER" | translate}}' (filter)="doFilter($event)" [currentValue]="currentTerm"></hbr-filter>
|
||||
<hbr-filter [withDivider]="true" filterPlaceholder='{{"AUDIT_LOG.FILTER_PLACEHOLDER" | translate}}' (filter)="doFilter($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>
|
||||
@ -47,6 +47,7 @@ export const LOG_STYLES: string = `
|
||||
|
||||
.action-head-pos {
|
||||
padding-right: 18px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
@ -54,7 +55,7 @@ export const LOG_STYLES: string = `
|
||||
}
|
||||
|
||||
.refresh-btn:hover {
|
||||
color: #00bfff;
|
||||
color: #007CBB;
|
||||
}
|
||||
|
||||
.custom-lines-button {
|
||||
|
@ -1,11 +1,18 @@
|
||||
export const REPLICATION_STYLE: string = `
|
||||
.refresh-btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.refresh-btn:hover {
|
||||
color: #007CBB;
|
||||
}
|
||||
|
||||
.option-left {
|
||||
padding-left: 16px;
|
||||
margin-top: 24px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
.option-right {
|
||||
padding-right: 16px;
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.option-left-down {
|
||||
|
@ -1,21 +1,21 @@
|
||||
export const REPLICATION_TEMPLATE: string = `
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="row flex-items-xs-between">
|
||||
<div class="row flex-items-xs-between" style="height:24px;">
|
||||
<div class="flex-xs-middle option-left">
|
||||
<button *ngIf="projectId" class="btn btn-link" (click)="openModal()"><clr-icon shape="add"></clr-icon> {{'REPLICATION.REPLICATION_RULE' | translate}}</button>
|
||||
<create-edit-rule [projectId]="projectId" (reload)="reloadRules($event)"></create-edit-rule>
|
||||
</div>
|
||||
<div class="flex-xs-middle option-right">
|
||||
<div class="select" style="float: left;">
|
||||
<div class="select" style="float: left; top: 9px;">
|
||||
<select (change)="doFilterRuleStatus($event)">
|
||||
<option *ngFor="let r of ruleStatus" value="{{r.key}}">{{r.description | translate}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<hbr-filter filterPlaceholder='{{"REPLICATION.FILTER_POLICIES_PLACEHOLDER" | translate}}' (filter)="doSearchRules($event)" [currentValue]="search.ruleName"></hbr-filter>
|
||||
<a href="javascript:void(0)" (click)="refreshRules()">
|
||||
<hbr-filter [withDivider]="true" filterPlaceholder='{{"REPLICATION.FILTER_POLICIES_PLACEHOLDER" | translate}}' (filter)="doSearchRules($event)" [currentValue]="search.ruleName"></hbr-filter>
|
||||
<span class="refresh-btn" (click)="refreshRules()">
|
||||
<clr-icon shape="refresh"></clr-icon>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -23,14 +23,14 @@ export const REPLICATION_TEMPLATE: string = `
|
||||
<hbr-list-replication-rule #listReplicationRule [projectId]="projectId" (selectOne)="selectOneRule($event)" (editOne)="openEditRule($event)" (reload)="reloadRules($event)" [loading]="loading" [withReplicationJob]="withReplicationJob" (redirect)="customRedirect($event)"></hbr-list-replication-rule>
|
||||
</div>
|
||||
<div *ngIf="withReplicationJob" class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="row flex-items-xs-between">
|
||||
<div class="row flex-items-xs-between" style="height:60px;">
|
||||
<h5 class="flex-items-xs-bottom option-left-down" style="margin-left: 14px;">{{'REPLICATION.REPLICATION_JOBS' | translate}}</h5>
|
||||
<div class="flex-items-xs-bottom option-right-down">
|
||||
<button class="btn btn-link" (click)="toggleSearchJobOptionalName(currentJobSearchOption)">{{toggleJobSearchOption[currentJobSearchOption] | translate}}</button>
|
||||
<hbr-filter filterPlaceholder='{{"REPLICATION.FILTER_JOBS_PLACEHOLDER" | translate}}' (filter)="doSearchJobs($event)" [currentValue]="search.repoName" ></hbr-filter>
|
||||
<a href="javascript:void(0)" (click)="refreshJobs()">
|
||||
<hbr-filter [withDivider]="true" filterPlaceholder='{{"REPLICATION.FILTER_JOBS_PLACEHOLDER" | translate}}' (filter)="doSearchJobs($event)" [currentValue]="search.repoName" ></hbr-filter>
|
||||
<span class="refresh-btn" (click)="refreshJobs()">
|
||||
<clr-icon shape="refresh"></clr-icon>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row flex-items-xs-right option-right" [hidden]="currentJobSearchOption === 0">
|
||||
@ -53,6 +53,7 @@ export const REPLICATION_TEMPLATE: string = `
|
||||
<clr-dg-column [clrDgSortBy]="creationTimeComparator">{{'REPLICATION.CREATION_TIME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgSortBy]="updateTimeComparator">{{'REPLICATION.END_TIME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'REPLICATION.LOGS' | translate}}</clr-dg-column>
|
||||
<clr-dg-placeholder>{{'REPLICATION.JOB_PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
||||
<clr-dg-row *clrDgItems="let j of jobs" [clrDgItem]='j'>
|
||||
<clr-dg-cell>{{j.repository}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{j.status}}</clr-dg-cell>
|
||||
|
@ -1,14 +1,17 @@
|
||||
export const REPOSITORY_STACKVIEW_STYLES: string = `
|
||||
.option-right {
|
||||
padding-right: 16px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.sub-grid-custom {
|
||||
position: relative;
|
||||
left: 40px;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
.refresh-btn:hover {
|
||||
color: #007CBB;
|
||||
}
|
||||
:host >>> .datagrid .datagrid-body .datagrid-row {
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
|
@ -1,11 +1,11 @@
|
||||
export const REPOSITORY_STACKVIEW_TEMPLATE: string = `
|
||||
<confirmation-dialog #confirmationDialog (confirmAction)="confirmDeletion($event)"></confirmation-dialog>
|
||||
<div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12" style="height: 24px;">
|
||||
<div class="row flex-items-xs-right option-right">
|
||||
<div class="flex-xs-middle">
|
||||
<hbr-filter filterPlaceholder="{{'REPOSITORY.FILTER_FOR_REPOSITORIES' | translate}}" (filter)="doSearchRepoNames($event)"></hbr-filter>
|
||||
<a href="javascript:void(0)" (click)="refresh()"><clr-icon shape="refresh"></clr-icon></a>
|
||||
<hbr-filter [withDivider]="true" filterPlaceholder="{{'REPOSITORY.FILTER_FOR_REPOSITORIES' | translate}}" (filter)="doSearchRepoNames($event)"></hbr-filter>
|
||||
<span class="refresh-btn" (click)="refresh()"><clr-icon shape="refresh"></clr-icon></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -21,7 +21,7 @@ export const REPOSITORY_STACKVIEW_TEMPLATE: string = `
|
||||
<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" class="sub-grid-custom" [repoName]="r.name" [registryUrl]="registryUrl" [withNotary]="withNotary" [hasSignedIn]="hasSignedIn" [hasProjectAdminRole]="hasProjectAdminRole" [projectId]="projectId" [isEmbedded]="true" (refreshRepo)="refresh($event)"></hbr-tag>
|
||||
<hbr-tag *clrIfExpanded ngProjectAs="clr-dg-row-detail" (tagClickEvent)="watchTagClickEvt($event)" class="sub-grid-custom" [repoName]="r.name" [registryUrl]="registryUrl" [withNotary]="withNotary" [hasSignedIn]="hasSignedIn" [hasProjectAdminRole]="hasProjectAdminRole" [projectId]="projectId" [isEmbedded]="true" (refreshRepo)="refresh($event)"></hbr-tag>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'REPOSITORY.OF' | translate}}
|
||||
@ -31,4 +31,6 @@ export const REPOSITORY_STACKVIEW_TEMPLATE: string = `
|
||||
</clr-datagrid>
|
||||
</div>
|
||||
</div>
|
||||
<confirmation-dialog #confirmationDialog (confirmAction)="confirmDeletion($event)"></confirmation-dialog>
|
||||
</div>
|
||||
`;
|
@ -1,4 +1,13 @@
|
||||
import { Component, Input, OnInit, ViewChild, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
Input,
|
||||
Output,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
EventEmitter
|
||||
} from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Comparator } from 'clarity-angular';
|
||||
|
||||
@ -21,6 +30,7 @@ import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation
|
||||
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';
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-repository-stackview',
|
||||
@ -34,6 +44,7 @@ export class RepositoryStackviewComponent implements OnInit {
|
||||
|
||||
@Input() hasSignedIn: boolean;
|
||||
@Input() hasProjectAdminRole: boolean;
|
||||
@Output() tagClickEvent = new EventEmitter<Tag>();
|
||||
|
||||
lastFilteredRepoName: string;
|
||||
repositories: Repository[];
|
||||
@ -120,4 +131,8 @@ export class RepositoryStackviewComponent implements OnInit {
|
||||
refresh() {
|
||||
this.retrieve();
|
||||
}
|
||||
|
||||
watchTagClickEvt(tag: Tag): void {
|
||||
this.tagClickEvent.emit(tag);
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ import { ErrorHandler } from '../error-handler/error-handler';
|
||||
import { Tag, VulnerabilitySummary } from '../service/interface';
|
||||
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
||||
import { TagService, TagDefaultService, ScanningResultService, ScanningResultDefaultService } from '../service/index';
|
||||
import { FilterComponent } from '../filter/index';
|
||||
|
||||
describe('TagDetailComponent (inline template)', () => {
|
||||
|
||||
@ -47,7 +48,8 @@ describe('TagDetailComponent (inline template)', () => {
|
||||
],
|
||||
declarations: [
|
||||
TagDetailComponent,
|
||||
ResultGridComponent
|
||||
ResultGridComponent,
|
||||
FilterComponent
|
||||
],
|
||||
providers: [
|
||||
ErrorHandler,
|
||||
|
@ -22,12 +22,13 @@ export const TAG_TEMPLATE = `
|
||||
<clr-dg-column [clrDgField]="'docker_version'">{{'REPOSITORY.DOCKER_VERSION' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'architecture'">{{'REPOSITORY.ARCHITECTURE' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'os'">{{'REPOSITORY.OS' | translate}}</clr-dg-column>
|
||||
<clr-dg-placeholder>{{'TGA.PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
||||
<clr-dg-row *clrDgItems="let t of tags" [clrDgItem]='t'>
|
||||
<clr-dg-action-overflow>
|
||||
<button class="action-item" (click)="showDigestId(t)">{{'REPOSITORY.COPY_DIGEST_ID' | translate}}</button>
|
||||
<button class="action-item" [hidden]="!hasProjectAdminRole" (click)="deleteTag(t)">{{'REPOSITORY.DELETE' | translate}}</button>
|
||||
</clr-dg-action-overflow>
|
||||
<clr-dg-cell>{{t.name}}</clr-dg-cell>
|
||||
<clr-dg-cell><a href="javascript:void(0)" (click)="onTagClick(t)">{{t.name}}</a></clr-dg-cell>
|
||||
<clr-dg-cell>docker pull {{registryUrl}}/{{repoName}}:{{t.name}}</clr-dg-cell>
|
||||
<clr-dg-cell *ngIf="withNotary" [ngSwitch]="t.signature !== null">
|
||||
<clr-icon shape="check" *ngSwitchCase="true" style="color: #1D5100;"></clr-icon>
|
||||
|
@ -51,6 +51,7 @@ export class TagComponent implements OnInit {
|
||||
@Input() withNotary: boolean;
|
||||
|
||||
@Output() refreshRepo = new EventEmitter<boolean>();
|
||||
@Output() tagClickEvent = new EventEmitter<Tag>();
|
||||
|
||||
tags: Tag[];
|
||||
|
||||
@ -105,7 +106,7 @@ export class TagComponent implements OnInit {
|
||||
this.errorHandler.error('Repo name cannot be unset.');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.retrieve();
|
||||
}
|
||||
|
||||
@ -161,7 +162,14 @@ export class TagComponent implements OnInit {
|
||||
this.showTagManifestOpened = true;
|
||||
}
|
||||
}
|
||||
|
||||
selectAndCopy($event: any) {
|
||||
$event.target.select();
|
||||
}
|
||||
|
||||
onTagClick(tag: Tag): void {
|
||||
if (tag) {
|
||||
this.tagClickEvent.emit(tag);
|
||||
}
|
||||
}
|
||||
}
|
@ -118,4 +118,9 @@ export class CustomComparator<T> implements Comparator<T> {
|
||||
}
|
||||
return comp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The default page size
|
||||
*/
|
||||
export const DEFAULT_PAGE_SIZE: number = 15;
|
@ -10,6 +10,7 @@ import { ScanningResultService, ScanningResultDefaultService } from '../service/
|
||||
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
||||
import { ErrorHandler } from '../error-handler/index';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
import { FilterComponent } from '../filter/index';
|
||||
|
||||
describe('ResultGridComponent (inline template)', () => {
|
||||
let component: ResultGridComponent;
|
||||
@ -26,7 +27,7 @@ describe('ResultGridComponent (inline template)', () => {
|
||||
imports: [
|
||||
SharedModule
|
||||
],
|
||||
declarations: [ResultGridComponent],
|
||||
declarations: [ResultGridComponent, FilterComponent],
|
||||
providers: [
|
||||
ErrorHandler,
|
||||
{ provide: SERVICE_CONFIG, useValue: testConfig },
|
||||
|
@ -38,4 +38,12 @@ export class ResultGridComponent implements OnInit {
|
||||
})
|
||||
.catch(error => { this.errorHandler.error(error) })
|
||||
}
|
||||
|
||||
filterVulnerabilities(terms: string): void {
|
||||
console.log(terms);
|
||||
}
|
||||
|
||||
refresh(): void {
|
||||
this.loadResults(this.tagId);
|
||||
}
|
||||
}
|
||||
|
@ -4,11 +4,9 @@ export const SCANNING_STYLES: string = `
|
||||
height: 24px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.bar-state {
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
.scanning-button {
|
||||
height: 24px;
|
||||
margin-top: 0px;
|
||||
@ -17,62 +15,58 @@ export const SCANNING_STYLES: string = `
|
||||
top: -6px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tip-wrapper {
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
max-height: 16px;
|
||||
max-width: 150px;
|
||||
}
|
||||
|
||||
.tip-position {
|
||||
margin-left: -4px;
|
||||
}
|
||||
|
||||
.tip-block {
|
||||
margin-left: -4px;
|
||||
}
|
||||
|
||||
.bar-block-high {
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
.bar-block-medium {
|
||||
background-color: orange;
|
||||
}
|
||||
|
||||
.bar-block-low {
|
||||
background-color: yellow;
|
||||
}
|
||||
|
||||
.bar-block-none {
|
||||
background-color: green;
|
||||
}
|
||||
|
||||
.bar-block-unknown {
|
||||
background-color: grey;
|
||||
}
|
||||
|
||||
.bar-tooltip-font {
|
||||
font-size: 13px;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.bar-tooltip-font-title {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.bar-summary {
|
||||
margin-top: 12px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.bar-scanning-time {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.bar-summary-item {
|
||||
margin-top: 3px;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
.option-right {
|
||||
padding-right: 16px;
|
||||
}
|
||||
.refresh-btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
.refresh-btn:hover {
|
||||
color: #007CBB;
|
||||
}
|
||||
`;
|
@ -42,7 +42,16 @@ export const TIP_COMPONENT_HTML: string = `
|
||||
`;
|
||||
|
||||
export const GRID_COMPONENT_HTML: string = `
|
||||
<div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12" style="height: 24px;">
|
||||
<div class="row flex-items-xs-right option-right">
|
||||
<div class="flex-xs-middle">
|
||||
<hbr-filter [withDivider]="true" filterPlaceholder="{{'VULNERABILITY.PLACEHOLDER' | translate}}" (filter)="filterVulnerabilities($event)"></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>
|
||||
<clr-dg-column [clrDgField]="'id'">{{'VULNERABILITY.GRID.COLUMN_ID' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'severity'">{{'VULNERABILITY.GRID.COLUMN_SEVERITY' | translate}}</clr-dg-column>
|
||||
@ -71,6 +80,7 @@ export const GRID_COMPONENT_HTML: string = `
|
||||
<clr-dg-pagination #pagination [clrDgPageSize]="25" [clrDgTotalItems]="scanningResults.length"></clr-dg-pagination>
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user