mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-22 18:25:56 +01:00
Add sort fun when filter label in tag page #4617
This commit is contained in:
parent
9bf38c2486
commit
b520addf08
@ -37,6 +37,7 @@ import {LabelColor} from "../shared/shared.const";
|
||||
selector: 'hbr-create-edit-label',
|
||||
template: CREATE_EDIT_LABEL_TEMPLATE,
|
||||
styles: [CREATE_EDIT_LABEL_STYLE],
|
||||
changeDetection: ChangeDetectionStrategy.Default
|
||||
})
|
||||
|
||||
export class CreateEditLabelComponent implements OnInit, OnDestroy {
|
||||
@ -63,6 +64,7 @@ export class CreateEditLabelComponent implements OnInit, OnDestroy {
|
||||
constructor(
|
||||
private labelService: LabelService,
|
||||
private errorHandler: ErrorHandler,
|
||||
private ref: ChangeDetectorRef
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
@ -81,6 +83,9 @@ export class CreateEditLabelComponent implements OnInit, OnDestroy {
|
||||
this.checkOnGoing = false;
|
||||
this.errorHandler.error(error)
|
||||
});
|
||||
setTimeout(() => {
|
||||
setInterval(() => this.ref.markForCheck(), 100);
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
@ -104,6 +109,7 @@ export class CreateEditLabelComponent implements OnInit, OnDestroy {
|
||||
openModal(): void {
|
||||
this.labelModel = this.initLabel();
|
||||
this.formShow = true;
|
||||
this.isLabelNameExist = false;
|
||||
this.labelId = 0;
|
||||
this.copeLabelModel = null;
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
export const TAG_DETAIL_STYLES: string = `
|
||||
.overview-section {
|
||||
padding-bottom: 36px;
|
||||
border-bottom: 1px solid #cccccc;
|
||||
}
|
||||
|
||||
.detail-section {
|
||||
@ -60,7 +59,7 @@ export const TAG_DETAIL_STYLES: string = `
|
||||
|
||||
.summary-block {
|
||||
margin-top: 24px;
|
||||
display: inline-flex;
|
||||
display: flex;
|
||||
flex-wrap: row wrap;
|
||||
}
|
||||
|
||||
|
@ -9,8 +9,8 @@ export const TAG_DETAIL_HTML: string = `
|
||||
<h2 class="custom-h2">{{repositoryId}}:{{tagDetails.name}}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="summary-block">
|
||||
<div class="image-summary">
|
||||
<div class="summary-block row">
|
||||
<div class="image-summary col-md-4 col-lg-3">
|
||||
<div class="flex-block">
|
||||
<div class="image-detail-label">
|
||||
<div>{{'TAG.AUTHOR' | translate }}</div>
|
||||
@ -28,7 +28,7 @@ export const TAG_DETAIL_HTML: string = `
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="withClair">
|
||||
<div *ngIf="withClair" class="col-md-4 col-lg-3">
|
||||
<div class="vulnerability">
|
||||
<hbr-vulnerability-bar [repoName]="repositoryId" [tagId]="tagDetails.name" [summary]="tagDetails.scan_overview"></hbr-vulnerability-bar>
|
||||
</div>
|
||||
|
@ -26,7 +26,7 @@ export const TAG_TEMPLATE = `
|
||||
<div class="form-group"><input type="text" placeholder="Filter labels" [(ngModel)]= "filterName" (keyup)="handleInputFilter()"></div>
|
||||
<div [hidden]='imageFilterLabels.length' style="padding-left:10px;">{{'LABEL.NO_LABELS' | translate }}</div>
|
||||
<div [hidden]='!imageFilterLabels.length' style='max-height:300px;overflow-y: auto;'>
|
||||
<button type="button" class="dropdown-item" *ngFor='let label of imageFilterLabels' (click)="rightFilterLabel(label)">
|
||||
<button type="button" class="dropdown-item" *ngFor='let label of imageFilterLabels' [hidden]="!label.show" (click)="rightFilterLabel(label)">
|
||||
<clr-icon shape="check" class='pull-left' [hidden]='!label.iconsShow'></clr-icon>
|
||||
<div class='labelDiv'><hbr-label-piece [label]="label.label" [labelWidth]="130"></hbr-label-piece></div>
|
||||
</button>
|
||||
@ -51,7 +51,7 @@ export const TAG_TEMPLATE = `
|
||||
<div class="form-group"><input type="text" placeholder="Filter labels" [(ngModel)]="stickName" (keyup)="handleStickInputFilter()"></div>
|
||||
<div [hidden]='imageStickLabels.length' style="padding-left:10px;">{{'LABEL.NO_LABELS' | translate }}</div>
|
||||
<div [hidden]='!imageStickLabels.length' style='max-height:300px;overflow-y: auto;'>
|
||||
<button type="button" class="dropdown-item" *ngFor='let label of imageStickLabels' (click)="stickLabel(label)">
|
||||
<button type="button" class="dropdown-item" *ngFor='let label of imageStickLabels' [hidden]='!label.show' (click)="stickLabel(label)">
|
||||
<clr-icon shape="check" class='pull-left' [hidden]='!label.iconsShow'></clr-icon>
|
||||
<div class='labelDiv'><hbr-label-piece [label]="label.label" [labelWidth]="130"></hbr-label-piece></div>
|
||||
</button>
|
||||
@ -69,7 +69,7 @@ export const TAG_TEMPLATE = `
|
||||
<clr-dg-column style="min-width: 130px;">{{'REPOSITORY.AUTHOR' | translate}}</clr-dg-column>
|
||||
<clr-dg-column style="width: 160px;"[clrDgSortBy]="createdComparator">{{'REPOSITORY.CREATED' | translate}}</clr-dg-column>
|
||||
<clr-dg-column style="width: 80px;" [clrDgField]="'docker_version'" *ngIf="!withClair">{{'REPOSITORY.DOCKER_VERSION' | translate}}</clr-dg-column>
|
||||
<clr-dg-column *ngIf="!withAdmiral" style="width: 140px;" [clrDgField]="'labels'">{{'REPOSITORY.LABELS' | translate}}</clr-dg-column>
|
||||
<clr-dg-column *ngIf="!withAdmiral" style="width: 140px;">{{'REPOSITORY.LABELS' | translate}}</clr-dg-column>
|
||||
<clr-dg-placeholder>{{'TAG.PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
||||
<clr-dg-row *clrDgItems="let t of tags" [clrDgItem]='t'>
|
||||
<clr-dg-cell class="truncated" style="width: 120px;">
|
||||
|
@ -60,12 +60,17 @@ import {BatchInfo, BathInfoChanges} from "../confirmation-dialog/confirmation-ba
|
||||
import {Observable} from "rxjs/Observable";
|
||||
import {LabelService} from "../service/label.service";
|
||||
import {Subject} from "rxjs/Subject";
|
||||
export interface LabelOpe {
|
||||
iconsShow: boolean;
|
||||
label: Label;
|
||||
show: boolean;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: "hbr-tag",
|
||||
template: TAG_TEMPLATE,
|
||||
styles: [TAG_STYLE],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
changeDetection: ChangeDetectionStrategy.Default
|
||||
})
|
||||
export class TagComponent implements OnInit, AfterViewInit {
|
||||
|
||||
@ -103,16 +108,16 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
copyFailed = false;
|
||||
selectedRow: Tag[] = [];
|
||||
|
||||
imageLabels: {[key: string]: boolean | Label | any}[] = [];
|
||||
imageStickLabels: {[key: string]: boolean | Label | any}[] = [];
|
||||
imageFilterLabels: {[key: string]: boolean | Label | any}[] = [];
|
||||
imageLabels: LabelOpe[] = [];
|
||||
imageStickLabels: LabelOpe[] = [];
|
||||
imageFilterLabels: LabelOpe[] = [];
|
||||
|
||||
labelListOpen = false;
|
||||
selectedTag: Tag[];
|
||||
labelNameFilter: Subject<string> = new Subject<string> ();
|
||||
stickLabelNameFilter: Subject<string> = new Subject<string> ();
|
||||
filterOnGoing: boolean;
|
||||
stickName = ''
|
||||
stickName = '';
|
||||
filterName = '';
|
||||
initFilter = {
|
||||
name: '',
|
||||
@ -120,7 +125,7 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
color: '',
|
||||
scope: '',
|
||||
project_id: 0,
|
||||
}
|
||||
};
|
||||
filterOneLabel: Label = this.initFilter;
|
||||
|
||||
|
||||
@ -163,13 +168,14 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
.subscribe((name: string) => {
|
||||
if (this.filterName.length) {
|
||||
this.filterOnGoing = true;
|
||||
this.imageFilterLabels = [];
|
||||
|
||||
this.imageLabels.forEach(data => {
|
||||
this.imageFilterLabels.forEach(data => {
|
||||
if (data.label.name.indexOf(this.filterName) !== -1) {
|
||||
this.imageFilterLabels.push(data);
|
||||
data.show = true;
|
||||
} else {
|
||||
data.show = false;
|
||||
}
|
||||
})
|
||||
});
|
||||
setTimeout(() => {
|
||||
setInterval(() => this.ref.markForCheck(), 200);
|
||||
}, 1000);
|
||||
@ -182,11 +188,12 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
.subscribe((name: string) => {
|
||||
if (this.stickName.length) {
|
||||
this.filterOnGoing = true;
|
||||
this.imageStickLabels = [];
|
||||
|
||||
this.imageLabels.forEach(data => {
|
||||
this.imageStickLabels.forEach(data => {
|
||||
if (data.label.name.indexOf(this.stickName) !== -1) {
|
||||
this.imageStickLabels.push(data);
|
||||
data.show = true;
|
||||
}else {
|
||||
data.show = false;
|
||||
}
|
||||
})
|
||||
setTimeout(() => {
|
||||
@ -273,14 +280,14 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
toPromise<Label[]>(this.labelService.getGLabels()).then((res: Label[]) => {
|
||||
if (res.length) {
|
||||
res.forEach(data => {
|
||||
this.imageLabels.push({'iconsShow': false, 'label': data});
|
||||
this.imageLabels.push({'iconsShow': false, 'label': data, 'show': true});
|
||||
});
|
||||
}
|
||||
|
||||
toPromise<Label[]>(this.labelService.getPLabels(this.projectId)).then((res1: Label[]) => {
|
||||
if (res1.length) {
|
||||
res1.forEach(data => {
|
||||
this.imageLabels.push({'iconsShow': false, 'label': data});
|
||||
this.imageLabels.push({'iconsShow': false, 'label': data, 'show': true});
|
||||
});
|
||||
}
|
||||
this.imageFilterLabels = clone(this.imageLabels);
|
||||
@ -294,24 +301,31 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
|
||||
labelSelectedChange(tag?: Tag[]): void {
|
||||
if (tag && tag[0].labels && tag[0].labels.length) {
|
||||
if (tag && tag[0].labels) {
|
||||
this.imageStickLabels.forEach(data => {
|
||||
data.iconsShow = false;
|
||||
data.show = true;
|
||||
})
|
||||
if (tag[0].labels.length) {
|
||||
tag[0].labels.forEach((labelInfo: Label) => {
|
||||
this.imageStickLabels.find(data => labelInfo.id === data['label'].id).iconsShow = true;
|
||||
let findedLabel = this.imageStickLabels.find(data => labelInfo.id === data['label'].id);
|
||||
this.imageStickLabels.splice(this.imageStickLabels.indexOf(findedLabel), 1);
|
||||
this.imageStickLabels.unshift(findedLabel);
|
||||
|
||||
findedLabel.iconsShow = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addLabels(tag: Tag[]): void {
|
||||
this.labelListOpen = true;
|
||||
this.selectedTag = tag;
|
||||
|
||||
this.stickName = '';
|
||||
this.labelSelectedChange(tag);
|
||||
}
|
||||
|
||||
stickLabel(labelInfo: {[key: string]: any | string[]}): void {
|
||||
stickLabel(labelInfo: LabelOpe): void {
|
||||
if (labelInfo && !labelInfo.iconsShow) {
|
||||
this.selectLabel(labelInfo);
|
||||
}
|
||||
@ -320,13 +334,18 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
}
|
||||
|
||||
selectLabel(labelInfo: {[key: string]: any | string[]}): void {
|
||||
selectLabel(labelInfo: LabelOpe): void {
|
||||
if (!this.inprogress) {
|
||||
this.inprogress = true;
|
||||
let labelId = labelInfo.label.id;
|
||||
this.selectedRow = this.selectedTag;
|
||||
toPromise<any>(this.tagService.addLabelToImages(this.repoName, this.selectedRow[0].name, labelId)).then(res => {
|
||||
this.refresh();
|
||||
|
||||
// set the selected label in front
|
||||
this.imageStickLabels.splice(this.imageStickLabels.indexOf(labelInfo), 1);
|
||||
this.imageStickLabels.unshift(labelInfo);
|
||||
|
||||
labelInfo.iconsShow = true;
|
||||
this.inprogress = false;
|
||||
}).catch(err => {
|
||||
@ -336,13 +355,15 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
}
|
||||
|
||||
unSelectLabel(labelInfo: {[key: string]: any | string[]}): void {
|
||||
unSelectLabel(labelInfo: LabelOpe): void {
|
||||
if (!this.inprogress) {
|
||||
this.inprogress = true;
|
||||
let labelId = labelInfo.label.id;
|
||||
this.selectedRow = this.selectedTag;
|
||||
toPromise<any>(this.tagService.deleteLabelToImages(this.repoName, this.selectedRow[0].name, labelId)).then(res => {
|
||||
this.refresh();
|
||||
// insert the unselected label to groups with the same icons
|
||||
this.sortOperation(this.imageStickLabels, labelInfo);
|
||||
labelInfo.iconsShow = false;
|
||||
this.inprogress = false;
|
||||
}).catch(err => {
|
||||
@ -352,7 +373,7 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
}
|
||||
|
||||
rightFilterLabel(labelInfo: {[key: string]: any | string[]}): void {
|
||||
rightFilterLabel(labelInfo: LabelOpe): void {
|
||||
if (labelInfo) {
|
||||
if (!labelInfo.iconsShow) {
|
||||
this.filterLabel(labelInfo);
|
||||
@ -362,8 +383,14 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
}
|
||||
|
||||
filterLabel(labelInfo: {[key: string]: any | string[]}): void {
|
||||
filterLabel(labelInfo: LabelOpe): void {
|
||||
let labelName = labelInfo.label.name;
|
||||
// insert the unselected label to groups with the same icons
|
||||
let preLabelInfo = this.imageFilterLabels.find(data => data.label.id === this.filterOneLabel.id);
|
||||
if (preLabelInfo) {
|
||||
this.sortOperation(this.imageFilterLabels, preLabelInfo);
|
||||
}
|
||||
|
||||
this.imageFilterLabels.filter(data => {
|
||||
if (data.label.name !== labelName) {
|
||||
data.iconsShow = false;
|
||||
@ -371,10 +398,11 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
data.iconsShow = true;
|
||||
}
|
||||
});
|
||||
|
||||
this.imageFilterLabels.splice(this.imageFilterLabels.indexOf(labelInfo), 1);
|
||||
this.imageFilterLabels.unshift(labelInfo);
|
||||
this.filterOneLabel = labelInfo.label;
|
||||
|
||||
// reload datagu
|
||||
// reload data
|
||||
this.currentPage = 1;
|
||||
let st: State = this.currentState;
|
||||
if (!st) {
|
||||
@ -392,10 +420,14 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
this.clrLoad(st);
|
||||
}
|
||||
|
||||
unFilterLabel(labelInfo: {[key: string]: any | string[]}): void {
|
||||
unFilterLabel(labelInfo: LabelOpe): void {
|
||||
// insert the unselected label to groups with the same icons
|
||||
this.sortOperation(this.imageFilterLabels, labelInfo);
|
||||
|
||||
this.filterOneLabel = this.initFilter;
|
||||
labelInfo.iconsShow = false;
|
||||
// reload datagu
|
||||
|
||||
// reload data
|
||||
this.currentPage = 1;
|
||||
let st: State = this.currentState;
|
||||
if (!st) {
|
||||
@ -416,7 +448,7 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
if (this.filterName.length) {
|
||||
this.labelNameFilter.next(this.filterName);
|
||||
}else {
|
||||
this.imageFilterLabels = clone(this.imageLabels);
|
||||
this.imageFilterLabels.every(data => data.show = true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -424,10 +456,28 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
if (this.stickName.length) {
|
||||
this.stickLabelNameFilter.next(this.stickName);
|
||||
}else {
|
||||
this.imageStickLabels = clone(this.imageLabels);
|
||||
this.imageStickLabels.every(data => data.show = true);
|
||||
}
|
||||
}
|
||||
|
||||
// insert the unselected label to groups with the same icons
|
||||
sortOperation(labelList: LabelOpe[], labelInfo: LabelOpe): void {
|
||||
labelList.some((data, i) => {
|
||||
if (!data.iconsShow) {
|
||||
if (data.label.scope === labelInfo.label.scope) {
|
||||
labelList.splice(i, 0, labelInfo);
|
||||
labelList.splice(labelList.indexOf(labelInfo, 0), 1);
|
||||
return true;
|
||||
}
|
||||
if (data.label.scope !== labelInfo.label.scope && i === labelList.length - 1) {
|
||||
labelList.push(labelInfo);
|
||||
labelList.splice(labelList.indexOf(labelInfo), 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
retrieve() {
|
||||
this.tags = [];
|
||||
let signatures: string[] = [] ;
|
||||
|
@ -11,6 +11,7 @@ import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
||||
import { ErrorHandler } from '../error-handler/index';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
import { FilterComponent } from '../filter/index';
|
||||
import {ChannelService} from "../channel/channel.service";
|
||||
|
||||
describe('ResultGridComponent (inline template)', () => {
|
||||
let component: ResultGridComponent;
|
||||
@ -30,6 +31,7 @@ describe('ResultGridComponent (inline template)', () => {
|
||||
declarations: [ResultGridComponent, FilterComponent],
|
||||
providers: [
|
||||
ErrorHandler,
|
||||
ChannelService,
|
||||
{ provide: SERVICE_CONFIG, useValue: testConfig },
|
||||
{ provide: ScanningResultService, useClass: ScanningResultDefaultService }
|
||||
]
|
||||
|
@ -9,6 +9,7 @@ import { ErrorHandler } from '../error-handler/index';
|
||||
import { toPromise } from '../utils';
|
||||
import { GRID_COMPONENT_HTML } from './scanning.html';
|
||||
import { SCANNING_STYLES } from './scanning.css';
|
||||
import {ChannelService} from "../channel/channel.service";
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-vulnerabilities-grid',
|
||||
@ -24,6 +25,7 @@ export class ResultGridComponent implements OnInit {
|
||||
|
||||
constructor(
|
||||
private scanningService: ScanningResultService,
|
||||
private channel: ChannelService,
|
||||
private errorHandler: ErrorHandler
|
||||
) { }
|
||||
|
||||
@ -74,4 +76,8 @@ export class ResultGridComponent implements OnInit {
|
||||
let reg = new RegExp('.*' + terms + '.*', 'i');
|
||||
return reg.test(testedValue);
|
||||
}
|
||||
|
||||
scanNow(): void {
|
||||
this.channel.publishScanEvent(this.repositoryId + "/" + this.tagId);
|
||||
}
|
||||
}
|
||||
|
@ -126,5 +126,10 @@ hr{
|
||||
.font-weight-600{
|
||||
font-weight:600;
|
||||
}
|
||||
|
||||
.rightPos{
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
right: 35px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
`;
|
@ -67,10 +67,10 @@ export const TIP_COMPONENT_HTML: string = `
|
||||
`;
|
||||
|
||||
export const GRID_COMPONENT_HTML: string = `
|
||||
<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">
|
||||
<div class="row" style="position:relative;">
|
||||
<div>
|
||||
<div class="row flex-items-xs-right rightPos">
|
||||
<div class="flex-xs-middle option-right">
|
||||
<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>
|
||||
@ -78,6 +78,9 @@ export const GRID_COMPONENT_HTML: string = `
|
||||
</div>
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<clr-datagrid>
|
||||
<clr-dg-action-bar>
|
||||
<button type="button" class="btn btn-sm btn-secondary" (click)="scanNow()"><clr-icon shape="shield-check" size="16"></clr-icon> {{'VULNERABILITY.SCAN_NOW' | translate}}</button>
|
||||
</clr-dg-action-bar>
|
||||
<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>
|
||||
<clr-dg-column [clrDgField]="'package'">{{'VULNERABILITY.GRID.COLUMN_PACKAGE' | translate}}</clr-dg-column>
|
||||
|
@ -30,7 +30,7 @@
|
||||
"clarity-icons": "^0.10.27",
|
||||
"clarity-ui": "^0.10.27",
|
||||
"core-js": "^2.4.1",
|
||||
"harbor-ui": "0.7.6",
|
||||
"harbor-ui": "0.7.7",
|
||||
"intl": "^1.2.5",
|
||||
"mutationobserver-shim": "^0.3.2",
|
||||
"ngx-cookie": "^1.0.0",
|
||||
|
Loading…
Reference in New Issue
Block a user