Improve global search component (#15462)

Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
孙世军 2021-08-24 17:04:37 +08:00 committed by GitHub
parent a8562b2934
commit eca3d82d9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 135 additions and 147 deletions

View File

@ -1,5 +1,5 @@
<form class="search"> <form class="search">
<label for="search_input"> <label for="search_input">
<input #globalSearchBox name="globalSearchBox" [(ngModel)]="searchTerm" id="search_input" type="text" (keyup)="search(globalSearchBox.value)" placeholder='{{placeholderText | translate}}'> <input autocomplete="off" #globalSearchBox name="globalSearchBox" [(ngModel)]="searchTerm" id="search_input" type="text" (keyup)="search(globalSearchBox.value)" placeholder='{{placeholderText | translate}}'>
</label> </label>
</form> </form>

View File

@ -1,4 +1,3 @@
import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
// Copyright (c) 2017 VMware, Inc. All Rights Reserved. // Copyright (c) 2017 VMware, Inc. All Rights Reserved.
// //
@ -13,20 +12,17 @@ import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import { Component, OnInit, OnDestroy } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { Subject , Subscription } from "rxjs"; import { Subject, Subscription } from "rxjs";
import { SearchTriggerService } from './search-trigger.service'; import { SearchTriggerService } from './search-trigger.service';
import { AppConfigService } from '../../../services/app-config.service'; import { AppConfigService } from '../../../services/app-config.service';
import { TranslateService } from "@ngx-translate/core";
import { SkinableConfig } from "../../../services/skinable-config.service";
import { Location } from '@angular/common';
import {TranslateService} from "@ngx-translate/core";
import {SkinableConfig} from "../../../services/skinable-config.service";
const deBounceTime = 500; // ms const deBounceTime = 500; // ms
const SEARCH_KEY: string = 'globalSearch';
@Component({ @Component({
selector: 'global-search', selector: 'global-search',
@ -43,13 +39,13 @@ export class GlobalSearchComponent implements OnInit, OnDestroy {
// To indicate if the result panel is opened // To indicate if the result panel is opened
isResPanelOpened: boolean = false; isResPanelOpened: boolean = false;
searchTerm: string = "";
placeholderText: string; placeholderText: string;
private _searchTerm = "";
constructor( constructor(
private searchTrigger: SearchTriggerService, private searchTrigger: SearchTriggerService,
private router: Router, private router: Router,
private activatedRoute: ActivatedRoute,
private location: Location,
private appConfigService: AppConfigService, private appConfigService: AppConfigService,
private translate: TranslateService, private translate: TranslateService,
private skinableConfig: SkinableConfig) { private skinableConfig: SkinableConfig) {
@ -71,8 +67,7 @@ export class GlobalSearchComponent implements OnInit, OnDestroy {
} }
this.searchSub = this.searchTerms.pipe( this.searchSub = this.searchTerms.pipe(
debounceTime(deBounceTime), debounceTime(deBounceTime))
distinctUntilChanged())
.subscribe(term => { .subscribe(term => {
this.searchTrigger.triggerSearch(term); this.searchTrigger.triggerSearch(term);
}); });
@ -83,6 +78,11 @@ export class GlobalSearchComponent implements OnInit, OnDestroy {
if (this.appConfigService.isIntegrationMode()) { if (this.appConfigService.isIntegrationMode()) {
this.placeholderText = "GLOBAL_SEARCH.PLACEHOLDER_VIC"; this.placeholderText = "GLOBAL_SEARCH.PLACEHOLDER_VIC";
} }
// init _searchTerm from queryParams
this._searchTerm = this.activatedRoute.snapshot.queryParams[SEARCH_KEY];
if (this._searchTerm) {
this.searchTerms.next(this._searchTerm);
}
} }
ngOnDestroy(): void { ngOnDestroy(): void {
@ -98,7 +98,24 @@ export class GlobalSearchComponent implements OnInit, OnDestroy {
// Handle the term inputting event // Handle the term inputting event
search(term: string): void { search(term: string): void {
// Send event even term is empty // Send event even term is empty
this.searchTerms.next(term.trim()); this.searchTerms.next(term.trim());
} }
get searchTerm(): string {
return this._searchTerm;
}
set searchTerm(s) {
let url: string;
if (s) {
url = this.router.createUrlTree([], {
relativeTo: this.activatedRoute,
queryParams: {[SEARCH_KEY]: s}
}).toString();
} else {
url = this.router.createUrlTree([], {
relativeTo: this.activatedRoute,
}).toString();
}
this.location.replaceState(url);
this._searchTerm = s;
}
} }

View File

@ -1,5 +1,5 @@
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Component, OnInit, Input } from '@angular/core'; import { Component, OnInit, Input, ChangeDetectionStrategy } from '@angular/core';
import { HelmChartSearchResultItem, HelmChartVersion, HelmChartMaintainer } from '../../../base/project/helm-chart/helm-chart-detail/helm-chart.interface.service'; import { HelmChartSearchResultItem, HelmChartVersion, HelmChartMaintainer } from '../../../base/project/helm-chart/helm-chart-detail/helm-chart.interface.service';
import { SearchTriggerService } from '../global-search/search-trigger.service'; import { SearchTriggerService } from '../global-search/search-trigger.service';
import { ProjectService } from "../../services"; import { ProjectService } from "../../services";
@ -7,11 +7,10 @@ import { ProjectService } from "../../services";
@Component({ @Component({
selector: 'list-chart-version-ro', selector: 'list-chart-version-ro',
templateUrl: './list-chart-version-ro.component.html' templateUrl: './list-chart-version-ro.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class ListChartVersionRoComponent implements OnInit { export class ListChartVersionRoComponent implements OnInit {
@Input() projectId: number;
@Input() charts: HelmChartSearchResultItem[]; @Input() charts: HelmChartSearchResultItem[];
constructor( constructor(

View File

@ -1,14 +1,15 @@
<clr-datagrid (clrDgRefresh)="refresh($event)"> <clr-datagrid>
<clr-dg-column>{{'PROJECT.NAME' | translate}}</clr-dg-column> <clr-dg-column>{{'PROJECT.NAME' | translate}}</clr-dg-column>
<clr-dg-column>{{'PROJECT.ACCESS_LEVEL' | translate}}</clr-dg-column> <clr-dg-column>{{'PROJECT.ACCESS_LEVEL' | translate}}</clr-dg-column>
<clr-dg-column>{{'PROJECT.REPO_COUNT'| translate}}</clr-dg-column> <clr-dg-column>{{'PROJECT.REPO_COUNT'| translate}}</clr-dg-column>
<clr-dg-column>{{'PROJECT.CREATION_TIME' | translate}}</clr-dg-column> <clr-dg-column>{{'PROJECT.CREATION_TIME' | translate}}</clr-dg-column>
<clr-dg-row *clrDgItems="let p of projects" [clrDgItem]="p"> <clr-dg-row *clrDgItems="let p of projects" [clrDgItem]="p">
<clr-dg-cell><a href="javascript:void(0)" (click)="goToLink(p.project_id)">{{p.name}}</a></clr-dg-cell> <clr-dg-cell><a href="javascript:void(0)" [routerLink]="getLink(p.project_id)">{{p.name}}</a></clr-dg-cell>
<clr-dg-cell>{{ (p.metadata.public === 'true' ? 'PROJECT.PUBLIC' : 'PROJECT.PRIVATE') | translate}}</clr-dg-cell> <clr-dg-cell>{{ (p.metadata.public === 'true' ? 'PROJECT.PUBLIC' : 'PROJECT.PRIVATE') | translate}}</clr-dg-cell>
<clr-dg-cell>{{p.repo_count}}</clr-dg-cell> <clr-dg-cell>{{p.repo_count}}</clr-dg-cell>
<clr-dg-cell>{{p.creation_time | harborDatetime: 'short'}}</clr-dg-cell> <clr-dg-cell>{{p.creation_time | harborDatetime: 'short'}}</clr-dg-cell>
</clr-dg-row> </clr-dg-row>
<clr-dg-placeholder>{{'PROJECT.NO_PROJECT' | translate }}</clr-dg-placeholder>
<clr-dg-footer> <clr-dg-footer>
<span *ngIf="projects?.length">{{pagination.firstItem + 1}} - {{pagination.lastItem +1 }} {{'PROJECT.OF' | translate}} </span> {{projects?.length}} {{'PROJECT.ITEMS' | translate}} <span *ngIf="projects?.length">{{pagination.firstItem + 1}} - {{pagination.lastItem +1 }} {{'PROJECT.OF' | translate}} </span> {{projects?.length}} {{'PROJECT.ITEMS' | translate}}
<clr-dg-pagination #pagination [clrDgPageSize]="5"></clr-dg-pagination> <clr-dg-pagination #pagination [clrDgPageSize]="5"></clr-dg-pagination>

View File

@ -1,47 +1,50 @@
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { ListProjectROComponent } from './list-project-ro.component'; import { ListProjectROComponent } from './list-project-ro.component';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { SharedTestingModule } from "../../shared.module";
import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations'; import { Project } from "../../../../../ng-swagger-gen/models/project";
import { ClarityModule } from '@clr/angular'; import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { RouterTestingModule } from '@angular/router/testing'; // mock a TestHostComponent for ListProjectROComponent
import { of } from 'rxjs'; @Component({
import { HttpClientTestingModule } from '@angular/common/http/testing'; template: `
import { SearchTriggerService } from '../global-search/search-trigger.service'; <list-project-ro [projects]="projects">
</list-project-ro>`
})
class TestHostComponent {
projects: Project[] = [];
}
describe('ListProjectROComponent', () => { describe('ListProjectROComponent', () => {
let component: ListProjectROComponent; let component: TestHostComponent;
let fixture: ComponentFixture<ListProjectROComponent>; let fixture: ComponentFixture<TestHostComponent>;
const mockSearchTriggerService = { const mockedProjects: Project[] = [
closeSearch: () => { } {
}; chart_count: 0,
name: "test1",
metadata: {},
project_id: 1,
repo_count: 1
},
{
chart_count: 0,
name: "test2",
metadata: {},
project_id: 2,
repo_count: 1
}
];
beforeEach(waitForAsync(() => { beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
schemas: [
CUSTOM_ELEMENTS_SCHEMA
],
imports: [ imports: [
BrowserAnimationsModule, SharedTestingModule
ClarityModule,
TranslateModule.forRoot(),
FormsModule,
RouterTestingModule,
NoopAnimationsModule,
HttpClientTestingModule
], ],
declarations: [ListProjectROComponent], declarations: [ListProjectROComponent,
providers: [ TestHostComponent],
TranslateService, }).compileComponents();
{ provide: SearchTriggerService, useValue: mockSearchTriggerService }
]
})
.compileComponents();
})); }));
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(ListProjectROComponent); fixture = TestBed.createComponent(TestHostComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
@ -49,4 +52,12 @@ describe('ListProjectROComponent', () => {
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
it('should render project list', async () => {
component.projects = mockedProjects;
fixture.detectChanges();
await fixture.whenStable();
const rows = fixture.nativeElement.querySelectorAll('clr-dg-row');
expect(rows.length).toEqual(2);
});
}); });

View File

@ -11,12 +11,8 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import { Component, EventEmitter, Output, Input, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { Router } from '@angular/router'; import { Project } from "../../../../../ng-swagger-gen/models/project";
import { State } from '../../services/interface';
import { SearchTriggerService } from '../global-search/search-trigger.service';
import { Project } from '../../../base/project/project';
@Component({ @Component({
selector: 'list-project-ro', selector: 'list-project-ro',
@ -25,21 +21,11 @@ import { Project } from '../../../base/project/project';
}) })
export class ListProjectROComponent { export class ListProjectROComponent {
@Input() projects: Project[]; @Input() projects: Project[];
@Output() paginate = new EventEmitter<State>();
constructor() {
constructor(
private searchTrigger: SearchTriggerService,
private router: Router) {}
goToLink(proId: number): void {
this.searchTrigger.closeSearch(true);
let linkUrl = ['harbor', 'projects', proId, 'repositories'];
this.router.navigate(linkUrl);
} }
refresh(state: State) { getLink(proId: number) {
this.paginate.emit(state); return `/harbor/projects/${proId}/repositories`;
} }
} }

View File

@ -1,12 +1,13 @@
<clr-datagrid (clrDgRefresh)="refresh($event)"> <clr-datagrid>
<clr-dg-column>{{'REPOSITORY.NAME' | translate}}</clr-dg-column> <clr-dg-column>{{'REPOSITORY.NAME' | translate}}</clr-dg-column>
<clr-dg-column>{{'REPOSITORY.ARTIFACTS_COUNT' | translate}}</clr-dg-column> <clr-dg-column>{{'REPOSITORY.ARTIFACTS_COUNT' | translate}}</clr-dg-column>
<clr-dg-column>{{'REPOSITORY.PULL_COUNT' | translate}}</clr-dg-column> <clr-dg-column>{{'REPOSITORY.PULL_COUNT' | translate}}</clr-dg-column>
<clr-dg-row *clrDgItems="let r of repositories" [clrDgItem]='r'> <clr-dg-row *clrDgItems="let r of repositories" [clrDgItem]='r'>
<clr-dg-cell><a href="javascript:void(0)" (click)="gotoLink(projectId || r.project_id, r.name || r.repository_name)">{{r.name || r.repository_name}}</a></clr-dg-cell> <clr-dg-cell><a href="javascript:void(0)" [routerLink]="getLink(r.project_id, r.name || r.repository_name)" [queryParams]="getQueryParams()">{{r.name || r.repository_name}}</a></clr-dg-cell>
<clr-dg-cell>{{r.artifact_count}}</clr-dg-cell> <clr-dg-cell>{{r.artifact_count}}</clr-dg-cell>
<clr-dg-cell>{{r.pull_count}}</clr-dg-cell> <clr-dg-cell>{{r.pull_count}}</clr-dg-cell>
</clr-dg-row> </clr-dg-row>
<clr-dg-placeholder>{{'REPOSITORY.PLACEHOLDER' | translate }}</clr-dg-placeholder>
<clr-dg-footer> <clr-dg-footer>
<span *ngIf="repositories?.length">{{pagination.firstItem + 1}} - {{pagination.lastItem +1 }} {{'REPOSITORY.OF' | translate}} </span> {{repositories?.length }} {{'REPOSITORY.ITEMS' | translate}} <span *ngIf="repositories?.length">{{pagination.firstItem + 1}} - {{pagination.lastItem +1 }} {{'REPOSITORY.OF' | translate}} </span> {{repositories?.length }} {{'REPOSITORY.ITEMS' | translate}}
<clr-dg-pagination #pagination [clrDgPageSize]="5"></clr-dg-pagination> <clr-dg-pagination #pagination [clrDgPageSize]="5"></clr-dg-pagination>

View File

@ -1,5 +1,3 @@
import {filter} from 'rxjs/operators';
// Copyright (c) 2017 VMware, Inc. All Rights Reserved. // Copyright (c) 2017 VMware, Inc. All Rights Reserved.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
@ -13,72 +11,39 @@ import {filter} from 'rxjs/operators';
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import { Component, Input, Output, OnDestroy, EventEmitter, ChangeDetectionStrategy, ChangeDetectorRef, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router'; import { Router } from '@angular/router';
import { State } from '../../services/interface';
import { Repository } from '../../../../../ng-swagger-gen/models/repository'; import { Repository } from '../../../../../ng-swagger-gen/models/repository';
import { SearchTriggerService } from '../global-search/search-trigger.service'; import { SearchTriggerService } from '../global-search/search-trigger.service';
import {Subscription} from "rxjs";
import { SessionService } from "../../services/session.service"; import { SessionService } from "../../services/session.service";
import { UN_LOGGED_PARAM } from "../../../account/sign-in/sign-in.service"; import { UN_LOGGED_PARAM } from "../../../account/sign-in/sign-in.service";
const YES: string = 'yes'; const YES: string = 'yes';
@Component({ @Component({
selector: 'list-repository-ro', selector: 'list-repository-ro',
templateUrl: 'list-repository-ro.component.html', templateUrl: 'list-repository-ro.component.html',
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class ListRepositoryROComponent implements OnInit, OnDestroy { export class ListRepositoryROComponent {
@Input() projectId: number;
@Input() repositories: Repository[]; @Input() repositories: Repository[];
@Output() paginate = new EventEmitter<State>();
routerSubscription: Subscription;
constructor( constructor(
private router: Router, private router: Router,
private searchTrigger: SearchTriggerService, private searchTrigger: SearchTriggerService,
private ref: ChangeDetectorRef,
private sessionService: SessionService) { private sessionService: SessionService) {
this.router.routeReuseStrategy.shouldReuseRoute = function() {
return false;
};
this.routerSubscription = this.router.events.pipe(filter(event => event instanceof NavigationEnd))
.subscribe((event) => {
// trick the Router into believing it's last link wasn't previously loaded
this.router.navigated = false;
// if you need to scroll back to top, here is the right place
window.scrollTo(0, 0);
});
} }
ngOnInit(): void { getLink(projectId: number, repoName: string) {
let hnd = setInterval(() => this.ref.markForCheck(), 100);
setTimeout(() => clearInterval(hnd), 1000);
}
ngOnDestroy(): void {
this.routerSubscription.unsubscribe();
}
refresh(state: State) {
if (this.repositories) {
this.paginate.emit(state);
}
}
public gotoLink(projectId: number, repoName: string): void {
this.searchTrigger.closeSearch(true);
let projectName = repoName.split('/')[0]; let projectName = repoName.split('/')[0];
let repositorieName = projectName ? repoName.substr(projectName.length + 1) : repoName; let repositorieName = projectName ? repoName.substr(projectName.length + 1) : repoName;
let linkUrl = ['harbor', 'projects', projectId, 'repositories', repositorieName ]; return `/harbor/projects/${projectId}/repositories/${repositorieName}`;
}
getQueryParams() {
if (this.sessionService.getCurrentUser()) { if (this.sessionService.getCurrentUser()) {
this.router.navigate(linkUrl); return null;
} else {// if not logged in and it's a public project, add param 'publicAndNotLogged'
this.router.navigate(linkUrl, {queryParams: {[UN_LOGGED_PARAM]: YES}});
} }
return {[UN_LOGGED_PARAM]: YES};
} }
} }

View File

@ -254,7 +254,8 @@
"PROXY_CACHE": "Proxy Cache", "PROXY_CACHE": "Proxy Cache",
"PROXY_CACHE_TOOLTIP": "Die Aktivierung der Funktion erlaubt es dem Projekt, als Cache für eine andere Registry Instanz zu dienen. Harbor unterstützt die Proxy Funktion nur für DockerHub, Docker Registry, Harbor, Aws ECR, Azure ACR, Quay und Google GCR.", "PROXY_CACHE_TOOLTIP": "Die Aktivierung der Funktion erlaubt es dem Projekt, als Cache für eine andere Registry Instanz zu dienen. Harbor unterstützt die Proxy Funktion nur für DockerHub, Docker Registry, Harbor, Aws ECR, Azure ACR, Quay und Google GCR.",
"ENDPOINT": "Endpunkt", "ENDPOINT": "Endpunkt",
"PROXY_CACHE_ENDPOINT": "Proxy Cache Endpunkt" "PROXY_CACHE_ENDPOINT": "Proxy Cache Endpunkt",
"NO_PROJECT": "We couldn't find any projects"
}, },
"PROJECT_DETAIL": { "PROJECT_DETAIL": {
"SUMMARY": "Zusammenfassung", "SUMMARY": "Zusammenfassung",

View File

@ -254,7 +254,8 @@
"PROXY_CACHE": "Proxy Cache", "PROXY_CACHE": "Proxy Cache",
"PROXY_CACHE_TOOLTIP": "Enable this to allow this project to act as a pull-through cache for a particular target registry instance. Harbor can only act a proxy for DockerHub, Docker Registry, Harbor, Aws ECR, Azure ACR, Quay and Google GCR registries.", "PROXY_CACHE_TOOLTIP": "Enable this to allow this project to act as a pull-through cache for a particular target registry instance. Harbor can only act a proxy for DockerHub, Docker Registry, Harbor, Aws ECR, Azure ACR, Quay and Google GCR registries.",
"ENDPOINT": "Endpoint", "ENDPOINT": "Endpoint",
"PROXY_CACHE_ENDPOINT": "Proxy Cache Endpoint" "PROXY_CACHE_ENDPOINT": "Proxy Cache Endpoint",
"NO_PROJECT": "We couldn't find any projects"
}, },
"PROJECT_DETAIL": { "PROJECT_DETAIL": {
"SUMMARY": "Summary", "SUMMARY": "Summary",

View File

@ -255,7 +255,8 @@
"PROXY_CACHE": "Proxy Cache", "PROXY_CACHE": "Proxy Cache",
"PROXY_CACHE_TOOLTIP": "Enable this to allow this project to act as a pull-through cache for a particular target registry instance. Harbor can only act a proxy for DockerHub, Docker Registry, Harbor, Aws ECR, Azure ACR, Quay and Google GCR registries.", "PROXY_CACHE_TOOLTIP": "Enable this to allow this project to act as a pull-through cache for a particular target registry instance. Harbor can only act a proxy for DockerHub, Docker Registry, Harbor, Aws ECR, Azure ACR, Quay and Google GCR registries.",
"ENDPOINT": "Endpoint", "ENDPOINT": "Endpoint",
"PROXY_CACHE_ENDPOINT": "Proxy Cache Endpoint" "PROXY_CACHE_ENDPOINT": "Proxy Cache Endpoint",
"NO_PROJECT": "We couldn't find any projects"
}, },
"PROJECT_DETAIL": { "PROJECT_DETAIL": {
"SUMMARY": "Summary", "SUMMARY": "Summary",

View File

@ -248,7 +248,8 @@
"PROXY_CACHE": "Proxy Cache", "PROXY_CACHE": "Proxy Cache",
"PROXY_CACHE_TOOLTIP": "Enable this to allow this project to act as a pull-through cache for a particular target registry instance. Harbor can only act a proxy for DockerHub, Docker Registry, Harbor, Aws ECR, Azure ACR, Quay and Google GCR registries.", "PROXY_CACHE_TOOLTIP": "Enable this to allow this project to act as a pull-through cache for a particular target registry instance. Harbor can only act a proxy for DockerHub, Docker Registry, Harbor, Aws ECR, Azure ACR, Quay and Google GCR registries.",
"ENDPOINT": "Endpoint", "ENDPOINT": "Endpoint",
"PROXY_CACHE_ENDPOINT": "Proxy Cache Endpoint" "PROXY_CACHE_ENDPOINT": "Proxy Cache Endpoint",
"NO_PROJECT": "We couldn't find any projects"
}, },
"PROJECT_DETAIL": { "PROJECT_DETAIL": {
"SUMMARY": "Summary", "SUMMARY": "Summary",

View File

@ -252,7 +252,8 @@
"PROXY_CACHE": "Proxy Cache", "PROXY_CACHE": "Proxy Cache",
"PROXY_CACHE_TOOLTIP": "Enable this to allow this project to act as a pull-through cache for a particular target registry instance. Harbor can only act a proxy for DockerHub, Docker Registry, Harbor, Aws ECR, Azure ACR, Quay and Google GCR registries.", "PROXY_CACHE_TOOLTIP": "Enable this to allow this project to act as a pull-through cache for a particular target registry instance. Harbor can only act a proxy for DockerHub, Docker Registry, Harbor, Aws ECR, Azure ACR, Quay and Google GCR registries.",
"ENDPOINT": "Endpoint", "ENDPOINT": "Endpoint",
"PROXY_CACHE_ENDPOINT": "Proxy Cache Endpoint" "PROXY_CACHE_ENDPOINT": "Proxy Cache Endpoint",
"NO_PROJECT": "We couldn't find any projects"
}, },
"PROJECT_DETAIL": { "PROJECT_DETAIL": {
"SUMMARY": "Summary", "SUMMARY": "Summary",

View File

@ -254,7 +254,8 @@
"PROXY_CACHE": "Proxy Cache", "PROXY_CACHE": "Proxy Cache",
"PROXY_CACHE_TOOLTIP": "Enable this to allow this project to act as a pull-through cache for a particular target registry instance. Harbor can only act a proxy for DockerHub, Docker Registry, Harbor, Aws ECR, Azure ACR, Quay and Google GCR registries.", "PROXY_CACHE_TOOLTIP": "Enable this to allow this project to act as a pull-through cache for a particular target registry instance. Harbor can only act a proxy for DockerHub, Docker Registry, Harbor, Aws ECR, Azure ACR, Quay and Google GCR registries.",
"ENDPOINT": "Endpoint", "ENDPOINT": "Endpoint",
"PROXY_CACHE_ENDPOINT": "Proxy Cache Endpoint" "PROXY_CACHE_ENDPOINT": "Proxy Cache Endpoint",
"NO_PROJECT": "We couldn't find any projects"
}, },
"PROJECT_DETAIL": { "PROJECT_DETAIL": {
"SUMMARY": "Özet", "SUMMARY": "Özet",

View File

@ -253,7 +253,8 @@
"PROXY_CACHE": "镜像代理", "PROXY_CACHE": "镜像代理",
"PROXY_CACHE_TOOLTIP": "开启此项,以使得该项目成为目标仓库的镜像代理.仅支持 DockerHub, Docker Registry, Harbor, Aws ECR, Azure ACR, Quay 和 Google GCR 类型的仓库", "PROXY_CACHE_TOOLTIP": "开启此项,以使得该项目成为目标仓库的镜像代理.仅支持 DockerHub, Docker Registry, Harbor, Aws ECR, Azure ACR, Quay 和 Google GCR 类型的仓库",
"ENDPOINT": "地址", "ENDPOINT": "地址",
"PROXY_CACHE_ENDPOINT": "镜像代理地址" "PROXY_CACHE_ENDPOINT": "镜像代理地址",
"NO_PROJECT": "未发现任何项目"
}, },
"PROJECT_DETAIL": { "PROJECT_DETAIL": {
"SUMMARY": "概要", "SUMMARY": "概要",

View File

@ -251,7 +251,8 @@
"PROXY_CACHE": "Proxy Cache", "PROXY_CACHE": "Proxy Cache",
"PROXY_CACHE_TOOLTIP": "Enable this to allow this project to act as a pull-through cache for a particular target registry instance. Harbor can only act a proxy for DockerHub, Docker Registry, Harbor, Aws ECR, Azure ACR, Quay and Google GCR registries.", "PROXY_CACHE_TOOLTIP": "Enable this to allow this project to act as a pull-through cache for a particular target registry instance. Harbor can only act a proxy for DockerHub, Docker Registry, Harbor, Aws ECR, Azure ACR, Quay and Google GCR registries.",
"ENDPOINT": "Endpoint", "ENDPOINT": "Endpoint",
"PROXY_CACHE_ENDPOINT": "Proxy Cache Endpoint" "PROXY_CACHE_ENDPOINT": "Proxy Cache Endpoint",
"NO_PROJECT": "We couldn't find any projects"
}, },
"PROJECT_DETAIL":{ "PROJECT_DETAIL":{
"SUMMARY": "概要", "SUMMARY": "概要",