Modify tag detail page and fix label bugs

This commit is contained in:
pfh 2018-03-27 10:12:37 +08:00
parent cbcca015b0
commit 42fa690f90
19 changed files with 104 additions and 68 deletions

View File

@ -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);
} }
} }

View File

@ -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;

View File

@ -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;
} }

View File

@ -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); });
} }
} }

View File

@ -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 {

View File

@ -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>

View File

@ -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");
}); });
})); }));

View File

@ -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 {

View File

@ -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;
}
`; `;

View File

@ -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>

View File

@ -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));

View File

@ -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 => {

View File

@ -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",

View File

@ -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,

View File

@ -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>

View File

@ -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]);
} }
} }

View File

@ -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",

View File

@ -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",

View File

@ -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": "标签",