mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-16 23:35:20 +01:00
Modify tag detail page and fix label bugs
This commit is contained in:
parent
cbcca015b0
commit
42fa690f90
@ -81,6 +81,7 @@ export class Configuration {
|
|||||||
token_expiration: NumberValueItem;
|
token_expiration: NumberValueItem;
|
||||||
cfg_expiration: NumberValueItem;
|
cfg_expiration: NumberValueItem;
|
||||||
scan_all_policy: ComplexValueItem;
|
scan_all_policy: ComplexValueItem;
|
||||||
|
read_only: BoolValueItem;
|
||||||
|
|
||||||
public constructor() {
|
public constructor() {
|
||||||
this.auth_mode = new StringValueItem("db_auth", true);
|
this.auth_mode = new StringValueItem("db_auth", true);
|
||||||
@ -116,5 +117,6 @@ export class Configuration {
|
|||||||
daily_time: 0
|
daily_time: 0
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
|
this.read_only = new BoolValueItem(false, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,8 +10,8 @@ export const CREATE_EDIT_LABEL_STYLE: string = `
|
|||||||
section{padding:.5rem 0;}
|
section{padding:.5rem 0;}
|
||||||
section> label{margin-left: 20px;}
|
section> label{margin-left: 20px;}
|
||||||
|
|
||||||
.dropdown-menu{display:inline-block;width:166px; padding:6px;}
|
.dropdown-menu {display:inline-block;width:166px; padding:6px;}
|
||||||
.dropdown-item{ display:inline-flex; margin:2px 4px;
|
.dropdown-menu .dropdown-item{ display:inline-flex; margin:2px 4px;
|
||||||
display: inline-block;padding: 0px; width:30px;height:24px; text-align: center;line-height: 24px;}
|
display: inline-block;padding: 0px; width:30px;height:24px; text-align: center;line-height: 24px;}
|
||||||
.btnColor{
|
.btnColor{
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
|
@ -16,7 +16,7 @@ import {
|
|||||||
Output,
|
Output,
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
OnDestroy,
|
OnDestroy,
|
||||||
Input, OnInit, ViewChild
|
Input, OnInit, ViewChild, ChangeDetectionStrategy, ChangeDetectorRef
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
|
|
||||||
@ -27,16 +27,15 @@ import { CREATE_EDIT_LABEL_TEMPLATE } from './create-edit-label.component.html';
|
|||||||
|
|
||||||
import {toPromise, clone, compareValue} from '../utils';
|
import {toPromise, clone, compareValue} from '../utils';
|
||||||
|
|
||||||
import {Subject} from "rxjs/Subject";
|
|
||||||
|
|
||||||
import {LabelService} from "../service/label.service";
|
import {LabelService} from "../service/label.service";
|
||||||
import {ErrorHandler} from "../error-handler/error-handler";
|
import {ErrorHandler} from "../error-handler/error-handler";
|
||||||
import {NgForm} from "@angular/forms";
|
import {NgForm} from "@angular/forms";
|
||||||
|
import {Subject} from "rxjs/Subject";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'hbr-create-edit-label',
|
selector: 'hbr-create-edit-label',
|
||||||
template: CREATE_EDIT_LABEL_TEMPLATE,
|
template: CREATE_EDIT_LABEL_TEMPLATE,
|
||||||
styles: [CREATE_EDIT_LABEL_STYLE]
|
styles: [CREATE_EDIT_LABEL_STYLE],
|
||||||
})
|
})
|
||||||
|
|
||||||
export class CreateEditLabelComponent implements OnInit, OnDestroy {
|
export class CreateEditLabelComponent implements OnInit, OnDestroy {
|
||||||
@ -46,12 +45,13 @@ export class CreateEditLabelComponent implements OnInit, OnDestroy {
|
|||||||
labelModel: Label = this.initLabel();
|
labelModel: Label = this.initLabel();
|
||||||
labelId = 0;
|
labelId = 0;
|
||||||
|
|
||||||
nameChecker: Subject<string> = new Subject<string>();
|
|
||||||
checkOnGoing: boolean;
|
checkOnGoing: boolean;
|
||||||
isLabelNameExist = false;
|
isLabelNameExist = false;
|
||||||
|
|
||||||
labelColor = ['#00ab9a', '#9da3db', '#be90d6', '#9b0d54', '#f52f22', '#747474', '#0095d3', '#f38b00', ' #62a420', '#89cbdf', '#004a70', '#9460b8'];
|
labelColor = ['#00ab9a', '#9da3db', '#be90d6', '#9b0d54', '#f52f22', '#747474', '#0095d3', '#f38b00', ' #62a420', '#89cbdf', '#004a70', '#9460b8'];
|
||||||
|
|
||||||
|
nameChecker = new Subject<string>();
|
||||||
|
|
||||||
labelForm: NgForm;
|
labelForm: NgForm;
|
||||||
@ViewChild('labelForm')
|
@ViewChild('labelForm')
|
||||||
currentForm: NgForm;
|
currentForm: NgForm;
|
||||||
@ -66,16 +66,12 @@ export class CreateEditLabelComponent implements OnInit, OnDestroy {
|
|||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.nameChecker.debounceTime(500).distinctUntilChanged().subscribe((name: string) => {
|
this.nameChecker.debounceTime(500).subscribe((name: string) => {
|
||||||
this.checkOnGoing = true;
|
this.checkOnGoing = true;
|
||||||
toPromise<Label[]>(this.labelService.getLabels(this.scope, this.projectId))
|
toPromise<Label[]>(this.labelService.getLabels(this.scope, this.projectId, name))
|
||||||
.then(targets => {
|
.then(targets => {
|
||||||
if (targets && targets.length) {
|
if (targets && targets.length) {
|
||||||
if (targets.find(m => m.name === name)) {
|
this.isLabelNameExist = true;
|
||||||
this.isLabelNameExist = true;
|
|
||||||
} else {
|
|
||||||
this.isLabelNameExist = false;
|
|
||||||
};
|
|
||||||
}else {
|
}else {
|
||||||
this.isLabelNameExist = false;
|
this.isLabelNameExist = false;
|
||||||
}
|
}
|
||||||
|
@ -140,6 +140,6 @@ export class RepositoryDefaultService extends RepositoryService {
|
|||||||
|
|
||||||
return this.http.delete(url, HTTP_JSON_OPTIONS).toPromise()
|
return this.http.delete(url, HTTP_JSON_OPTIONS).toPromise()
|
||||||
.then(response => response)
|
.then(response => response)
|
||||||
.catch(error => { Promise.reject(error); });
|
.catch(error => {return Promise.reject(error); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
export const TAG_DETAIL_STYLES: string = `
|
export const TAG_DETAIL_STYLES: string = `
|
||||||
.overview-section {
|
.overview-section {
|
||||||
background-color: white;
|
|
||||||
padding-bottom: 36px;
|
padding-bottom: 36px;
|
||||||
border-bottom: 1px solid #cccccc;
|
border-bottom: 1px solid #cccccc;
|
||||||
}
|
}
|
||||||
@ -78,27 +77,37 @@ export const TAG_DETAIL_STYLES: string = `
|
|||||||
padding-left: 24px;
|
padding-left: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vulnerabilities-info .third-column {
|
.third-column {
|
||||||
margin-left: 36px;
|
margin-left: 36px;
|
||||||
}
|
}
|
||||||
|
.vulnerability{
|
||||||
|
margin-left: 50px;
|
||||||
|
margin-top: -12px;
|
||||||
|
margin-bottom: 20px;}
|
||||||
|
|
||||||
.vulnerabilities-info .second-column,
|
.vulnerabilities-info .second-column {
|
||||||
.vulnerabilities-info .fourth-column {
|
|
||||||
text-align: left;
|
text-align: left;
|
||||||
margin-left: 6px;
|
margin-left: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fourth-column{
|
||||||
|
float: left;
|
||||||
|
margin-left:20px;}
|
||||||
|
|
||||||
.vulnerabilities-info .second-row {
|
.vulnerabilities-info .second-row {
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-title {
|
.detail-title {
|
||||||
font-weight: 500;
|
float:left;
|
||||||
|
font-weight: 600;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.image-detail-label {
|
.image-detail-label {
|
||||||
text-align: right;
|
margin-right: 10px;
|
||||||
|
text-align: left;
|
||||||
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.image-detail-value {
|
.image-detail-value {
|
||||||
|
@ -7,26 +7,22 @@ export const TAG_DETAIL_HTML: string = `
|
|||||||
</div>
|
</div>
|
||||||
<div class="title-block">
|
<div class="title-block">
|
||||||
<div class="tag-name">
|
<div class="tag-name">
|
||||||
<h1>{{tagDetails.name}}</h1>
|
<h1>{{repositoryId}}:{{tagDetails.name}}</h1>
|
||||||
</div>
|
|
||||||
<div class="tag-timestamp">
|
|
||||||
{{'TAG.CREATION_TIME_PREFIX' | translate }} {{tagDetails.created | date }} {{'TAG.CREATOR_PREFIX' | translate }} {{author | translate}}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="summary-block">
|
<div class="summary-block">
|
||||||
<div class="image-summary">
|
<div class="image-summary">
|
||||||
<div class="detail-title">
|
|
||||||
{{'TAG.IMAGE_DETAILS' | translate }}
|
|
||||||
</div>
|
|
||||||
<div class="flex-block">
|
<div class="flex-block">
|
||||||
<div class="image-detail-label">
|
<div class="image-detail-label">
|
||||||
|
<div>{{'TAG.AUTHOR' | translate }}</div>
|
||||||
<div>{{'TAG.ARCHITECTURE' | translate }}</div>
|
<div>{{'TAG.ARCHITECTURE' | translate }}</div>
|
||||||
<div>{{'TAG.OS' | translate }}</div>
|
<div>{{'TAG.OS' | translate }}</div>
|
||||||
<div>{{'TAG.DOCKER_VERSION' | translate }}</div>
|
<div>{{'TAG.DOCKER_VERSION' | translate }}</div>
|
||||||
<div>{{'TAG.SCAN_COMPLETION_TIME' | translate }}</div>
|
<div>{{'TAG.SCAN_COMPLETION_TIME' | translate }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="image-detail-value">
|
<div class="image-detail-value">
|
||||||
|
<div>{{author | translate}}</div>
|
||||||
<div>{{tagDetails.architecture}}</div>
|
<div>{{tagDetails.architecture}}</div>
|
||||||
<div>{{tagDetails.os}}</div>
|
<div>{{tagDetails.os}}</div>
|
||||||
<div>{{tagDetails.docker_version}}</div>
|
<div>{{tagDetails.docker_version}}</div>
|
||||||
@ -35,8 +31,8 @@ export const TAG_DETAIL_HTML: string = `
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="detail-title">
|
<div class="vulnerability">
|
||||||
{{'TAG.IMAGE_VULNERABILITIES' | translate }}
|
<hbr-vulnerability-bar [repoName]="repositoryId" [tagId]="tagDetails.name" [summary]="tagDetails.scan_overview"></hbr-vulnerability-bar>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-block vulnerabilities-info">
|
<div class="flex-block vulnerabilities-info">
|
||||||
<div>
|
<div>
|
||||||
@ -46,12 +42,6 @@ export const TAG_DETAIL_HTML: string = `
|
|||||||
<div class="second-row">
|
<div class="second-row">
|
||||||
<clr-icon shape="exclamation-triangle" size="24" class="tip-icon-medium"></clr-icon>
|
<clr-icon shape="exclamation-triangle" size="24" class="tip-icon-medium"></clr-icon>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="second-column">
|
|
||||||
<div>{{highCount}} {{'VULNERABILITY.SEVERITY.HIGH' | translate }}</div>
|
|
||||||
<div class="second-row">{{mediumCount}} {{'VULNERABILITY.SEVERITY.MEDIUM' | translate }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="third-column">
|
|
||||||
<div>
|
<div>
|
||||||
<clr-icon shape="play" size="20" class="tip-icon-low rotate-90"></clr-icon>
|
<clr-icon shape="play" size="20" class="tip-icon-low rotate-90"></clr-icon>
|
||||||
</div>
|
</div>
|
||||||
@ -59,11 +49,20 @@ export const TAG_DETAIL_HTML: string = `
|
|||||||
<clr-icon shape="help" size="18" style="margin-left: 2px;"></clr-icon>
|
<clr-icon shape="help" size="18" style="margin-left: 2px;"></clr-icon>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="fourth-column">
|
<div class="second-column">
|
||||||
<div>{{lowCount}} {{'VULNERABILITY.SEVERITY.LOW' | translate }}</div>
|
<div>{{highCount}} {{'VULNERABILITY.SEVERITY.HIGH' | translate }}{{'TAG.LEVEL_VULNERABILITIES' | translate }}</div>
|
||||||
<div class="second-row">{{unknownCount}} {{'VULNERABILITY.SEVERITY.UNKNOWN' | translate }}</div>
|
<div class="second-row">{{mediumCount}} {{'VULNERABILITY.SEVERITY.MEDIUM' | translate }}{{'TAG.LEVEL_VULNERABILITIES' | translate }}</div>
|
||||||
|
<div>{{lowCount}} {{'VULNERABILITY.SEVERITY.LOW' | translate }}{{'TAG.LEVEL_VULNERABILITIES' | translate }}</div>
|
||||||
|
<div class="second-row">{{unknownCount}} {{'VULNERABILITY.SEVERITY.UNKNOWN' | translate }}{{'TAG.LEVEL_VULNERABILITIES' | translate }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div *ngIf="tagDetails.labels.length">
|
||||||
|
<div class="third-column detail-title">{{'TAG.LABELS' | translate }}</div>
|
||||||
|
<div class="fourth-column">
|
||||||
|
<div *ngFor="let label of tagDetails.labels" style="margin-bottom: 2px;"><hbr-label-piece [label]="label"></hbr-label-piece></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
@ -10,6 +10,11 @@ import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
|||||||
import { TagService, TagDefaultService, ScanningResultService, ScanningResultDefaultService } from '../service/index';
|
import { TagService, TagDefaultService, ScanningResultService, ScanningResultDefaultService } from '../service/index';
|
||||||
import { FilterComponent } from '../filter/index';
|
import { FilterComponent } from '../filter/index';
|
||||||
import { VULNERABILITY_SCAN_STATUS } from '../utils';
|
import { VULNERABILITY_SCAN_STATUS } from '../utils';
|
||||||
|
import {VULNERABILITY_DIRECTIVES} from "../vulnerability-scanning/index";
|
||||||
|
import {LabelPieceComponent} from "../label-piece/label-piece.component";
|
||||||
|
import {JobLogViewerComponent} from "../job-log-viewer/job-log-viewer.component";
|
||||||
|
import {ChannelService} from "../channel/channel.service";
|
||||||
|
import {JobLogService, JobLogDefaultService} from "../service/job-log.service";
|
||||||
|
|
||||||
describe('TagDetailComponent (inline template)', () => {
|
describe('TagDetailComponent (inline template)', () => {
|
||||||
|
|
||||||
@ -66,10 +71,16 @@ describe('TagDetailComponent (inline template)', () => {
|
|||||||
declarations: [
|
declarations: [
|
||||||
TagDetailComponent,
|
TagDetailComponent,
|
||||||
ResultGridComponent,
|
ResultGridComponent,
|
||||||
|
VULNERABILITY_DIRECTIVES,
|
||||||
|
LabelPieceComponent,
|
||||||
|
JobLogViewerComponent,
|
||||||
FilterComponent
|
FilterComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
ErrorHandler,
|
ErrorHandler,
|
||||||
|
ChannelService,
|
||||||
|
JobLogDefaultService,
|
||||||
|
{provide: JobLogService, useClass: JobLogDefaultService},
|
||||||
{ provide: SERVICE_CONFIG, useValue: config },
|
{ provide: SERVICE_CONFIG, useValue: config },
|
||||||
{ provide: TagService, useClass: TagDefaultService },
|
{ provide: TagService, useClass: TagDefaultService },
|
||||||
{ provide: ScanningResultService, useClass: ScanningResultDefaultService }
|
{ provide: ScanningResultService, useClass: ScanningResultDefaultService }
|
||||||
@ -119,7 +130,7 @@ describe('TagDetailComponent (inline template)', () => {
|
|||||||
|
|
||||||
let el: HTMLElement = fixture.nativeElement.querySelector('.tag-name');
|
let el: HTMLElement = fixture.nativeElement.querySelector('.tag-name');
|
||||||
expect(el).toBeTruthy();
|
expect(el).toBeTruthy();
|
||||||
expect(el.textContent.trim()).toEqual('nginx');
|
expect(el.textContent.trim()).toEqual('mock_repo:nginx');
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -133,7 +144,7 @@ describe('TagDetailComponent (inline template)', () => {
|
|||||||
expect(el).toBeTruthy();
|
expect(el).toBeTruthy();
|
||||||
let el2: HTMLElement = el.querySelector('div');
|
let el2: HTMLElement = el.querySelector('div');
|
||||||
expect(el2).toBeTruthy();
|
expect(el2).toBeTruthy();
|
||||||
expect(el2.textContent).toEqual("amd64");
|
expect(el2.textContent).toEqual("steven");
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -147,7 +158,7 @@ describe('TagDetailComponent (inline template)', () => {
|
|||||||
expect(el).toBeTruthy();
|
expect(el).toBeTruthy();
|
||||||
let el2: HTMLElement = el.querySelector('div');
|
let el2: HTMLElement = el.querySelector('div');
|
||||||
expect(el2).toBeTruthy();
|
expect(el2).toBeTruthy();
|
||||||
expect(el2.textContent.trim()).toEqual("13 VULNERABILITY.SEVERITY.HIGH");
|
expect(el2.textContent.trim()).toEqual("13 VULNERABILITY.SEVERITY.HIGHTAG.LEVEL_VULNERABILITIES");
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import { TAG_DETAIL_HTML } from './tag-detail.component.html';
|
|||||||
import { TagService, Tag, VulnerabilitySeverity } from '../service/index';
|
import { TagService, Tag, VulnerabilitySeverity } from '../service/index';
|
||||||
import { toPromise } from '../utils';
|
import { toPromise } from '../utils';
|
||||||
import { ErrorHandler } from '../error-handler/index';
|
import { ErrorHandler } from '../error-handler/index';
|
||||||
|
import {Label} from "../service/interface";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'hbr-tag-detail',
|
selector: 'hbr-tag-detail',
|
||||||
@ -19,6 +20,7 @@ export class TagDetailComponent implements OnInit {
|
|||||||
_mediumCount: number = 0;
|
_mediumCount: number = 0;
|
||||||
_lowCount: number = 0;
|
_lowCount: number = 0;
|
||||||
_unknownCount: number = 0;
|
_unknownCount: number = 0;
|
||||||
|
labels: Label;
|
||||||
|
|
||||||
@Input() tagId: string;
|
@Input() tagId: string;
|
||||||
@Input() repositoryId: string;
|
@Input() repositoryId: string;
|
||||||
@ -74,7 +76,7 @@ export class TagDetailComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onBack(): void {
|
onBack(): void {
|
||||||
this.backEvt.emit(this.tagId);
|
this.backEvt.emit(this.repositoryId);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPackageText(count: number): string {
|
getPackageText(count: number): string {
|
||||||
|
@ -66,4 +66,11 @@ export const TAG_STYLE = `
|
|||||||
:host >>> .signpost-content-body{padding:0 .4rem;}
|
:host >>> .signpost-content-body{padding:0 .4rem;}
|
||||||
:host >>> .signpost-content-header{display:none;}
|
:host >>> .signpost-content-header{display:none;}
|
||||||
.filterLabelPiece{position: absolute; bottom :0px;z-index:1;}
|
.filterLabelPiece{position: absolute; bottom :0px;z-index:1;}
|
||||||
|
.dropdown .dropdown-toggle.btn {
|
||||||
|
padding-right: 1rem;
|
||||||
|
border-left-width: 0;
|
||||||
|
border-right-width: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
margin-top: -2px;
|
||||||
|
}
|
||||||
`;
|
`;
|
@ -21,7 +21,7 @@ export const TAG_TEMPLATE = `
|
|||||||
<hbr-filter [withDivider]="true" filterPlaceholder="{{'TAG.FILTER_FOR_TAGS' | translate}}" (filter)="doSearchTagNames($event)" [currentValue]="lastFilteredTagName" clrDropdownTrigger></hbr-filter>
|
<hbr-filter [withDivider]="true" filterPlaceholder="{{'TAG.FILTER_FOR_TAGS' | translate}}" (filter)="doSearchTagNames($event)" [currentValue]="lastFilteredTagName" clrDropdownTrigger></hbr-filter>
|
||||||
<clr-dropdown-menu clrPosition="bottom-left" *clrIfOpen>
|
<clr-dropdown-menu clrPosition="bottom-left" *clrIfOpen>
|
||||||
<div style='display:grid'>
|
<div style='display:grid'>
|
||||||
<label class="dropdown-header">{{'REPOSITORY.ADD_TO_IMAGE' | translate}}</label>
|
<label class="dropdown-header">{{'REPOSITORY.FILTER_BY_LABEL' | translate}}</label>
|
||||||
<div class="form-group"><input type="text" placeholder="Filter labels" #labelNamePiece (keyup)="handleInputFilter(labelNamePiece.value)"></div>
|
<div class="form-group"><input type="text" placeholder="Filter labels" #labelNamePiece (keyup)="handleInputFilter(labelNamePiece.value)"></div>
|
||||||
<div [hidden]='imageFilterLabels.length'>{{'LABEL.NO_LABELS' | translate }}</div>
|
<div [hidden]='imageFilterLabels.length'>{{'LABEL.NO_LABELS' | translate }}</div>
|
||||||
<div [hidden]='!imageFilterLabels.length' style='max-height:300px;overflow-y: auto;'>
|
<div [hidden]='!imageFilterLabels.length' style='max-height:300px;overflow-y: auto;'>
|
||||||
@ -52,10 +52,10 @@ export const TAG_TEMPLATE = `
|
|||||||
<div class="form-group"><input type="text" placeholder="Filter labels" #stickLabelNamePiece (keyup)="handleStickInputFilter(stickLabelNamePiece.value)"></div>
|
<div class="form-group"><input type="text" placeholder="Filter labels" #stickLabelNamePiece (keyup)="handleStickInputFilter(stickLabelNamePiece.value)"></div>
|
||||||
<div [hidden]='imageStickLabels.length'>{{'LABEL.NO_LABELS' | translate }}</div>
|
<div [hidden]='imageStickLabels.length'>{{'LABEL.NO_LABELS' | translate }}</div>
|
||||||
<div [hidden]='!imageStickLabels.length' style='max-height:300px;overflow-y: auto;'>
|
<div [hidden]='!imageStickLabels.length' style='max-height:300px;overflow-y: auto;'>
|
||||||
<button type="button" class="dropdown-item" *ngFor='let label of imageStickLabels' (click)="label.iconsShow = true; selectLabel(label)">
|
<button type="button" class="dropdown-item" *ngFor='let label of imageStickLabels' (click)="selectLabel(label); label.iconsShow = true">
|
||||||
<clr-icon shape="check" class='pull-left' [hidden]='!label.iconsShow'></clr-icon>
|
<clr-icon shape="check" class='pull-left' [hidden]='!label.iconsShow'></clr-icon>
|
||||||
<div class='labelDiv'><hbr-label-piece [label]="label.label"></hbr-label-piece></div>
|
<div class='labelDiv'><hbr-label-piece [label]="label.label"></hbr-label-piece></div>
|
||||||
<clr-icon shape="times-circle" class='pull-right' [hidden]='!label.iconsShow' (click)="$event.stopPropagation(); label.iconsShow = false; unSelectLabel(label)"></clr-icon>
|
<clr-icon shape="times-circle" class='pull-right' [hidden]='!label.iconsShow' (click)="$event.stopPropagation(); unSelectLabel(label); label.iconsShow = false"></clr-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -140,13 +140,6 @@ describe('TagComponent (inline template)', () => {
|
|||||||
|
|
||||||
labelService = fixture.debugElement.injector.get(LabelService);
|
labelService = fixture.debugElement.injector.get(LabelService);
|
||||||
|
|
||||||
/*spyLabels = spyOn(labelService, 'getLabels').and.callFake(function (param) {
|
|
||||||
if (param === 'g') {
|
|
||||||
return Promise.resolve(mockLabels);
|
|
||||||
}else {
|
|
||||||
Promise.resolve(mockLabels1)
|
|
||||||
}
|
|
||||||
})*/
|
|
||||||
spyLabels = spyOn(labelService, 'getGLabels').and.returnValues(Promise.resolve(mockLabels));
|
spyLabels = spyOn(labelService, 'getGLabels').and.returnValues(Promise.resolve(mockLabels));
|
||||||
spyLabels1 = spyOn(labelService, 'getPLabels').and.returnValues(Promise.resolve(mockLabels1));
|
spyLabels1 = spyOn(labelService, 'getPLabels').and.returnValues(Promise.resolve(mockLabels1));
|
||||||
|
|
||||||
|
@ -180,11 +180,11 @@ export class TagComponent implements OnInit, AfterViewInit {
|
|||||||
.subscribe((name: string) => {
|
.subscribe((name: string) => {
|
||||||
if (name && name.length) {
|
if (name && name.length) {
|
||||||
this.filterOnGoing = true;
|
this.filterOnGoing = true;
|
||||||
this.imageFilterLabels = [];
|
this.imageStickLabels = [];
|
||||||
|
|
||||||
this.imageLabels.forEach(data => {
|
this.imageLabels.forEach(data => {
|
||||||
if (data.label.name.indexOf(name) !== -1) {
|
if (data.label.name.indexOf(name) !== -1) {
|
||||||
this.imageFilterLabels.push(data);
|
this.imageStickLabels.push(data);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -302,7 +302,7 @@ export class TagComponent implements OnInit, AfterViewInit {
|
|||||||
this.selectedChange(tag);
|
this.selectedChange(tag);
|
||||||
}
|
}
|
||||||
selectLabel(labelInfo: {[key: string]: any | string[]}): void {
|
selectLabel(labelInfo: {[key: string]: any | string[]}): void {
|
||||||
if (labelInfo && labelInfo.iconsShow) {
|
if (labelInfo && !labelInfo.iconsShow) {
|
||||||
let labelId = labelInfo.label.id;
|
let labelId = labelInfo.label.id;
|
||||||
this.selectedRow = this.selectedTag;
|
this.selectedRow = this.selectedTag;
|
||||||
toPromise<any>(this.tagService.addLabelToImages(this.repoName, this.selectedRow[0].name, labelId)).then(res => {
|
toPromise<any>(this.tagService.addLabelToImages(this.repoName, this.selectedRow[0].name, labelId)).then(res => {
|
||||||
@ -314,7 +314,7 @@ export class TagComponent implements OnInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unSelectLabel(labelInfo: {[key: string]: any | string[]}): void {
|
unSelectLabel(labelInfo: {[key: string]: any | string[]}): void {
|
||||||
if (labelInfo && !labelInfo.iconsShow) {
|
if (labelInfo && labelInfo.iconsShow) {
|
||||||
let labelId = labelInfo.label.id;
|
let labelId = labelInfo.label.id;
|
||||||
this.selectedRow = this.selectedTag;
|
this.selectedRow = this.selectedTag;
|
||||||
toPromise<any>(this.tagService.deleteLabelToImages(this.repoName, this.selectedRow[0].name, labelId)).then(res => {
|
toPromise<any>(this.tagService.deleteLabelToImages(this.repoName, this.selectedRow[0].name, labelId)).then(res => {
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
"clarity-icons": "^0.10.17",
|
"clarity-icons": "^0.10.17",
|
||||||
"clarity-ui": "^0.10.27",
|
"clarity-ui": "^0.10.27",
|
||||||
"core-js": "^2.4.1",
|
"core-js": "^2.4.1",
|
||||||
"harbor-ui": "0.6.53",
|
"harbor-ui": "0.6.57",
|
||||||
"intl": "^1.2.5",
|
"intl": "^1.2.5",
|
||||||
"mutationobserver-shim": "^0.3.2",
|
"mutationobserver-shim": "^0.3.2",
|
||||||
"ngx-cookie": "^1.0.0",
|
"ngx-cookie": "^1.0.0",
|
||||||
|
@ -108,6 +108,14 @@ const harborRoutes: Routes = [
|
|||||||
projectResolver: ProjectRoutingResolver
|
projectResolver: ProjectRoutingResolver
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'projects/:id/repositories/:repo/tags/:tag',
|
||||||
|
component: TagDetailPageComponent,
|
||||||
|
canActivate: [MemberGuard],
|
||||||
|
resolve: {
|
||||||
|
projectResolver: ProjectRoutingResolver
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'projects/:id',
|
path: 'projects/:id',
|
||||||
component: ProjectDetailComponent,
|
component: ProjectDetailComponent,
|
||||||
@ -124,10 +132,6 @@ const harborRoutes: Routes = [
|
|||||||
path: 'repositories/:repo/tags',
|
path: 'repositories/:repo/tags',
|
||||||
component: TagRepositoryComponent,
|
component: TagRepositoryComponent,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'repositories/:repo/tags/:tag',
|
|
||||||
component: TagDetailPageComponent
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'replications',
|
path: 'replications',
|
||||||
component: ReplicationPageComponent,
|
component: ReplicationPageComponent,
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
<div style="margin-top: 24px;">
|
<div>
|
||||||
<hbr-tag-detail (backEvt)="goBack($event)" [tagId]="tagId" [repositoryId]="repositoryId"></hbr-tag-detail>
|
<hbr-tag-detail (backEvt)="goBack($event)" [tagId]="tagId" [repositoryId]="repositoryId"></hbr-tag-detail>
|
||||||
</div>
|
</div>
|
@ -32,10 +32,10 @@ export class TagDetailPageComponent implements OnInit {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.repositoryId = this.route.snapshot.params["repo"];
|
this.repositoryId = this.route.snapshot.params["repo"];
|
||||||
this.tagId = this.route.snapshot.params["tag"];
|
this.tagId = this.route.snapshot.params["tag"];
|
||||||
this.projectId = this.route.snapshot.parent.params["id"];
|
this.projectId = this.route.snapshot.params["id"];
|
||||||
}
|
}
|
||||||
|
|
||||||
goBack(tag: string): void {
|
goBack(tag: string): void {
|
||||||
this.router.navigate(["harbor", "projects", this.projectId, "repositories"]);
|
this.router.navigate(["harbor", "projects", this.projectId, "repositories", tag]);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -415,6 +415,7 @@
|
|||||||
"IMAGE": "Images",
|
"IMAGE": "Images",
|
||||||
"LABELS": ":labels",
|
"LABELS": ":labels",
|
||||||
"ADD_TO_IMAGE": "Add labels to this image",
|
"ADD_TO_IMAGE": "Add labels to this image",
|
||||||
|
"FILTER_BY_LABEL": "Filter projects by label",
|
||||||
"ADD_LABELS": "Add labels"
|
"ADD_LABELS": "Add labels"
|
||||||
},
|
},
|
||||||
"ALERT": {
|
"ALERT": {
|
||||||
@ -438,6 +439,8 @@
|
|||||||
"REPLICATION": "Replication",
|
"REPLICATION": "Replication",
|
||||||
"EMAIL": "Email",
|
"EMAIL": "Email",
|
||||||
"LABEL": "Label",
|
"LABEL": "Label",
|
||||||
|
"REPOSITORY": "Repository",
|
||||||
|
"REPO_READ_ONLY": "Repository Read Only",
|
||||||
"SYSTEM": "System Settings",
|
"SYSTEM": "System Settings",
|
||||||
"VULNERABILITY": "Vulnerability",
|
"VULNERABILITY": "Vulnerability",
|
||||||
"CONFIRM_TITLE": "Confirm to cancel",
|
"CONFIRM_TITLE": "Confirm to cancel",
|
||||||
|
@ -413,7 +413,7 @@
|
|||||||
"INFO": "Información",
|
"INFO": "Información",
|
||||||
"NO_INFO": "Sin información de descripción para este repositorio",
|
"NO_INFO": "Sin información de descripción para este repositorio",
|
||||||
"IMAGE": "Imágenes",
|
"IMAGE": "Imágenes",
|
||||||
"LABELS": ":labels",
|
"LABELS": "Labels",
|
||||||
"ADD_TO_IMAGE": "Add labels to this image",
|
"ADD_TO_IMAGE": "Add labels to this image",
|
||||||
"ADD_LABELS": "Add labels"
|
"ADD_LABELS": "Add labels"
|
||||||
},
|
},
|
||||||
@ -438,6 +438,8 @@
|
|||||||
"REPLICATION": "Replicación",
|
"REPLICATION": "Replicación",
|
||||||
"EMAIL": "Email",
|
"EMAIL": "Email",
|
||||||
"LABEL": "Label",
|
"LABEL": "Label",
|
||||||
|
"REPOSITORY": "Repository",
|
||||||
|
"REPO_READ_ONLY": "Repository Read Only",
|
||||||
"SYSTEM": "Opciones del Sistema",
|
"SYSTEM": "Opciones del Sistema",
|
||||||
"VULNERABILITY": "Vulnerability",
|
"VULNERABILITY": "Vulnerability",
|
||||||
"CONFIRM_TITLE": "Confirma cancelación",
|
"CONFIRM_TITLE": "Confirma cancelación",
|
||||||
@ -613,9 +615,12 @@
|
|||||||
"OS": "OS",
|
"OS": "OS",
|
||||||
"SCAN_COMPLETION_TIME": "Scan Completed",
|
"SCAN_COMPLETION_TIME": "Scan Completed",
|
||||||
"IMAGE_VULNERABILITIES": "Image Vulnerabilities",
|
"IMAGE_VULNERABILITIES": "Image Vulnerabilities",
|
||||||
|
"LEVEL_VULNERABILITIES": "Level Vulnerabilities",
|
||||||
"PLACEHOLDER": "We couldn't find any tags!",
|
"PLACEHOLDER": "We couldn't find any tags!",
|
||||||
"COPY_ERROR": "Copy failed, please try to manually copy.",
|
"COPY_ERROR": "Copy failed, please try to manually copy.",
|
||||||
"FILTER_FOR_TAGS": "Etiquetas de filtro"
|
"FILTER_FOR_TAGS": "Etiquetas de filtro",
|
||||||
|
"AUTHOR": "Author",
|
||||||
|
"LABELS": "LABELS"
|
||||||
},
|
},
|
||||||
"LABEL": {
|
"LABEL": {
|
||||||
"LABEL": "Label",
|
"LABEL": "Label",
|
||||||
|
@ -438,6 +438,8 @@
|
|||||||
"REPLICATION": "复制",
|
"REPLICATION": "复制",
|
||||||
"EMAIL": "邮箱",
|
"EMAIL": "邮箱",
|
||||||
"LABEL": "标签",
|
"LABEL": "标签",
|
||||||
|
"REPOSITORY": "仓库",
|
||||||
|
"REPO_READ_ONLY": "仓库只读",
|
||||||
"SYSTEM": "系统设置",
|
"SYSTEM": "系统设置",
|
||||||
"VULNERABILITY": "漏洞",
|
"VULNERABILITY": "漏洞",
|
||||||
"CONFIRM_TITLE": "确认取消",
|
"CONFIRM_TITLE": "确认取消",
|
||||||
@ -613,9 +615,12 @@
|
|||||||
"OS": "操作系统",
|
"OS": "操作系统",
|
||||||
"SCAN_COMPLETION_TIME": "扫描完成时间",
|
"SCAN_COMPLETION_TIME": "扫描完成时间",
|
||||||
"IMAGE_VULNERABILITIES": "镜像缺陷",
|
"IMAGE_VULNERABILITIES": "镜像缺陷",
|
||||||
|
"LEVEL_VULNERABILITIES": "缺陷等级",
|
||||||
"PLACEHOLDER": "未发现任何标签!",
|
"PLACEHOLDER": "未发现任何标签!",
|
||||||
"COPY_ERROR": "拷贝失败,请尝试手动拷贝。",
|
"COPY_ERROR": "拷贝失败,请尝试手动拷贝。",
|
||||||
"FILTER_FOR_TAGS": "过滤项目"
|
"FILTER_FOR_TAGS": "过滤项目",
|
||||||
|
"AUTHOR": "作者",
|
||||||
|
"LABELS": "标签"
|
||||||
},
|
},
|
||||||
"LABEL": {
|
"LABEL": {
|
||||||
"LABEL": "标签",
|
"LABEL": "标签",
|
||||||
|
Loading…
Reference in New Issue
Block a user