mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-18 14:47:38 +01:00
Merge pull request #10436 from AllForNothing/nightly
Improve tag-retention and project search function
This commit is contained in:
commit
a3e84380fa
@ -15,7 +15,7 @@
|
|||||||
<option value="2">{{projectTypes[2] | translate}}</option>
|
<option value="2">{{projectTypes[2] | translate}}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<hbr-filter [withDivider]="true" filterPlaceholder='{{"PROJECT.FILTER_PLACEHOLDER" | translate}}' (filterEvt)="doSearchProjects($event)"
|
<hbr-filter [withDivider]="true" filterPlaceholder='{{"PROJECT.FILTER_PLACEHOLDER" | translate}}'
|
||||||
[currentValue]="projectName"></hbr-filter>
|
[currentValue]="projectName"></hbr-filter>
|
||||||
<span class="refresh-btn" (click)="refresh()">
|
<span class="refresh-btn" (click)="refresh()">
|
||||||
<clr-icon shape="refresh"></clr-icon>
|
<clr-icon shape="refresh"></clr-icon>
|
||||||
|
@ -10,6 +10,10 @@ import { HttpClientTestingModule } from '@angular/common/http/testing';
|
|||||||
import { ConfigurationService } from '../config/config.service';
|
import { ConfigurationService } from '../config/config.service';
|
||||||
import { SessionService } from "../shared/session.service";
|
import { SessionService } from "../shared/session.service";
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
|
import { delay } from 'rxjs/operators';
|
||||||
|
import { ProjectService } from '../../lib/services';
|
||||||
|
import { MessageHandlerService } from '../shared/message-handler/message-handler.service';
|
||||||
|
import { FilterComponent } from '../../lib/components/filter/filter.component';
|
||||||
describe('ProjectComponent', () => {
|
describe('ProjectComponent', () => {
|
||||||
let component: ProjectComponent;
|
let component: ProjectComponent;
|
||||||
let fixture: ComponentFixture<ProjectComponent>;
|
let fixture: ComponentFixture<ProjectComponent>;
|
||||||
@ -210,6 +214,19 @@ describe('ProjectComponent', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const mockProjectService = {
|
||||||
|
listProjects() {
|
||||||
|
return of({
|
||||||
|
body: []
|
||||||
|
}).pipe(delay(0));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const mockMessageHandlerService = {
|
||||||
|
refresh() {
|
||||||
|
},
|
||||||
|
showSuccess() {
|
||||||
|
},
|
||||||
|
};
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
schemas: [
|
schemas: [
|
||||||
@ -224,11 +241,16 @@ describe('ProjectComponent', () => {
|
|||||||
NoopAnimationsModule,
|
NoopAnimationsModule,
|
||||||
HttpClientTestingModule
|
HttpClientTestingModule
|
||||||
],
|
],
|
||||||
declarations: [ProjectComponent],
|
declarations: [
|
||||||
|
ProjectComponent,
|
||||||
|
FilterComponent
|
||||||
|
],
|
||||||
providers: [
|
providers: [
|
||||||
TranslateService,
|
TranslateService,
|
||||||
{ provide: SessionService, useValue: mockSessionService },
|
{ provide: SessionService, useValue: mockSessionService },
|
||||||
{ provide: ConfigurationService, useValue: mockConfigurationService },
|
{ provide: ConfigurationService, useValue: mockConfigurationService },
|
||||||
|
{ provide: ProjectService, useValue: mockProjectService },
|
||||||
|
{ provide: MessageHandlerService, useValue: mockMessageHandlerService },
|
||||||
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
@ -11,21 +11,27 @@
|
|||||||
// 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, ViewChild } from '@angular/core';
|
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||||
import { CreateProjectComponent } from './create-project/create-project.component';
|
import { CreateProjectComponent } from './create-project/create-project.component';
|
||||||
import { ListProjectComponent } from './list-project/list-project.component';
|
import { ListProjectComponent } from './list-project/list-project.component';
|
||||||
import { ProjectTypes } from '../shared/shared.const';
|
import { ProjectTypes } from '../shared/shared.const';
|
||||||
import { ConfigurationService } from '../config/config.service';
|
import { ConfigurationService } from '../config/config.service';
|
||||||
import { SessionService } from "../shared/session.service";
|
import { SessionService } from "../shared/session.service";
|
||||||
import { QuotaHardInterface } from "../../lib/services";
|
import { ProjectService, QuotaHardInterface, Repository, RequestQueryParams } from "../../lib/services";
|
||||||
import { Configuration } from "../../lib/components/config/config";
|
import { Configuration } from "../../lib/components/config/config";
|
||||||
|
import { FilterComponent } from '../../lib/components/filter/filter.component';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { debounceTime, distinctUntilChanged, finalize, switchMap } from 'rxjs/operators';
|
||||||
|
import { calculatePage, doFiltering, doSorting } from '../../lib/utils/utils';
|
||||||
|
import { Project } from './project';
|
||||||
|
import { MessageHandlerService } from '../shared/message-handler/message-handler.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'project',
|
selector: 'project',
|
||||||
templateUrl: 'project.component.html',
|
templateUrl: 'project.component.html',
|
||||||
styleUrls: ['./project.component.scss']
|
styleUrls: ['./project.component.scss']
|
||||||
})
|
})
|
||||||
export class ProjectComponent implements OnInit {
|
export class ProjectComponent implements OnInit, OnDestroy {
|
||||||
projectTypes = ProjectTypes;
|
projectTypes = ProjectTypes;
|
||||||
quotaObj: QuotaHardInterface;
|
quotaObj: QuotaHardInterface;
|
||||||
@ViewChild(CreateProjectComponent, {static: false})
|
@ViewChild(CreateProjectComponent, {static: false})
|
||||||
@ -48,10 +54,15 @@ export class ProjectComponent implements OnInit {
|
|||||||
window.sessionStorage['projectTypeValue'] = +_project;
|
window.sessionStorage['projectTypeValue'] = +_project;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ViewChild(FilterComponent, {static: true})
|
||||||
|
filterComponent: FilterComponent;
|
||||||
|
searchSub: Subscription;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public configService: ConfigurationService,
|
public configService: ConfigurationService,
|
||||||
private session: SessionService
|
private session: SessionService,
|
||||||
|
private proService: ProjectService,
|
||||||
|
private msgHandler: MessageHandlerService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
@ -62,7 +73,47 @@ export class ProjectComponent implements OnInit {
|
|||||||
if (this.isSystemAdmin) {
|
if (this.isSystemAdmin) {
|
||||||
this.getConfigration();
|
this.getConfigration();
|
||||||
}
|
}
|
||||||
|
if (!this.searchSub) {
|
||||||
|
this.searchSub = this.filterComponent.filterTerms.pipe(
|
||||||
|
debounceTime(500),
|
||||||
|
distinctUntilChanged(),
|
||||||
|
switchMap(projectName => {
|
||||||
|
// reset project list
|
||||||
|
this.listProject.currentPage = 1;
|
||||||
|
this.listProject.searchKeyword = projectName;
|
||||||
|
this.listProject.selectedRow = [];
|
||||||
|
this.loading = true;
|
||||||
|
let passInFilteredType: number = undefined;
|
||||||
|
if (this.listProject.filteredType > 0) {
|
||||||
|
passInFilteredType = this.listProject.filteredType - 1;
|
||||||
}
|
}
|
||||||
|
return this.proService.listProjects( this.listProject.searchKeyword,
|
||||||
|
passInFilteredType, this.listProject.currentPage, this.listProject.pageSize)
|
||||||
|
.pipe(finalize(() => {
|
||||||
|
this.loading = false;
|
||||||
|
}));
|
||||||
|
})).subscribe(response => {
|
||||||
|
// Get total count
|
||||||
|
if (response.headers) {
|
||||||
|
let xHeader: string = response.headers.get("X-Total-Count");
|
||||||
|
if (xHeader) {
|
||||||
|
this.listProject.totalCount = parseInt(xHeader, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.listProject.projects = response.body as Project[];
|
||||||
|
}, error => {
|
||||||
|
this.msgHandler.handleError(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
if (this.searchSub) {
|
||||||
|
this.searchSub.unsubscribe();
|
||||||
|
this.searchSub = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getConfigration() {
|
getConfigration() {
|
||||||
this.configService.getConfiguration()
|
this.configService.getConfiguration()
|
||||||
.subscribe((configurations: Configuration) => {
|
.subscribe((configurations: Configuration) => {
|
||||||
@ -72,6 +123,7 @@ export class ProjectComponent implements OnInit {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public get isSystemAdmin(): boolean {
|
public get isSystemAdmin(): boolean {
|
||||||
let account = this.session.getCurrentUser();
|
let account = this.session.getCurrentUser();
|
||||||
return account != null && account.has_admin_role;
|
return account != null && account.has_admin_role;
|
||||||
@ -86,11 +138,6 @@ export class ProjectComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doSearchProjects(projectName: string): void {
|
|
||||||
this.projectName = projectName;
|
|
||||||
this.listProject.doSearchProject(this.projectName);
|
|
||||||
}
|
|
||||||
|
|
||||||
doFilterProjects(): void {
|
doFilterProjects(): void {
|
||||||
this.listProject.doFilterProject(+this.selecteType);
|
this.listProject.doFilterProject(+this.selecteType);
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,9 @@ const SCHEDULE_TYPE = {
|
|||||||
HOURLY: "Hourly",
|
HOURLY: "Hourly",
|
||||||
CUSTOM: "Custom"
|
CUSTOM: "Custom"
|
||||||
};
|
};
|
||||||
|
const RUNNING: string = "Running";
|
||||||
|
const PENDING: string = "pending";
|
||||||
|
const TIMEOUT: number = 5000;
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tag-retention',
|
selector: 'tag-retention',
|
||||||
templateUrl: './tag-retention.component.html',
|
templateUrl: './tag-retention.component.html',
|
||||||
@ -341,6 +344,14 @@ export class TagRetentionComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
this.historyList = response.body as Array<any>;
|
this.historyList = response.body as Array<any>;
|
||||||
TagRetentionComponent.calculateDuration(this.historyList);
|
TagRetentionComponent.calculateDuration(this.historyList);
|
||||||
|
if (this.historyList && this.historyList.length
|
||||||
|
&& this.historyList.some(item => {
|
||||||
|
return item.status === RUNNING || item.status === PENDING;
|
||||||
|
})) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.loadLog();
|
||||||
|
}, TIMEOUT);
|
||||||
|
}
|
||||||
}, error => {
|
}, error => {
|
||||||
this.errorHandler.error(error);
|
this.errorHandler.error(error);
|
||||||
});
|
});
|
||||||
@ -362,6 +373,7 @@ export class TagRetentionComponent implements OnInit {
|
|||||||
this.tagRetentionService.getProjectInfo(this.projectId).subscribe(
|
this.tagRetentionService.getProjectInfo(this.projectId).subscribe(
|
||||||
response => {
|
response => {
|
||||||
this.retentionId = response.metadata.retention_id;
|
this.retentionId = response.metadata.retention_id;
|
||||||
|
this.refreshList();
|
||||||
this.getRetention();
|
this.getRetention();
|
||||||
}, error => {
|
}, error => {
|
||||||
this.loadingRule = false;
|
this.loadingRule = false;
|
||||||
@ -435,6 +447,7 @@ export class TagRetentionComponent implements OnInit {
|
|||||||
return this.tagRetentionService.getI18nKey(str);
|
return this.tagRetentionService.getI18nKey(str);
|
||||||
}
|
}
|
||||||
clrLoad() {
|
clrLoad() {
|
||||||
|
|
||||||
this.refreshList();
|
this.refreshList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
> .nav-item {
|
> .nav-item {
|
||||||
> button {
|
> button {
|
||||||
box-shadow: 0 -3px 0 $mode-link-color2 inset;
|
box-shadow: 0 -3px 0 $mode-link-color2 inset;
|
||||||
color: 0077b8;
|
color: #0077b8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,3 +116,8 @@ clr-dg-action-overflow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
hbr-tag {
|
||||||
|
.color-green {
|
||||||
|
color: $light-color-green !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -18,5 +18,6 @@ $select-back-color: #acbac3;
|
|||||||
$label-form-color: #212129;
|
$label-form-color: #212129;
|
||||||
$fill-color1: #ccc;
|
$fill-color1: #ccc;
|
||||||
$right-status-fill-color: white;
|
$right-status-fill-color: white;
|
||||||
|
$light-color-green: #4cd400;
|
||||||
|
|
||||||
@import "./common.scss";
|
@import "./common.scss";
|
@ -20,4 +20,5 @@ $select-back-color: $mode-background-color;
|
|||||||
|
|
||||||
$label-form-color: $mode-background-color3;
|
$label-form-color: $mode-background-color3;
|
||||||
$right-status-fill-color: #1d5100;
|
$right-status-fill-color: #1d5100;
|
||||||
|
$light-color-green: $right-status-fill-color;
|
||||||
@import "./common.scss";
|
@import "./common.scss";
|
@ -1229,7 +1229,7 @@
|
|||||||
"RULE_TEMPLATE_6": "最近#天被拉取过的镜像",
|
"RULE_TEMPLATE_6": "最近#天被拉取过的镜像",
|
||||||
"RULE_TEMPLATE_7": "最近#天被推送过的镜像",
|
"RULE_TEMPLATE_7": "最近#天被推送过的镜像",
|
||||||
"SCHEDULE": "定时任务",
|
"SCHEDULE": "定时任务",
|
||||||
"SCHEDULE_WARNING": "执行保留策略会将会删除受影响的镜像,且不可恢复。请在制定定时任务前仔细检查所有保留规则。",
|
"SCHEDULE_WARNING": "执行保留策略将会删除受影响的镜像,且不可恢复。请在制定定时任务前仔细检查所有保留规则。",
|
||||||
"EXISTING_RULE": "规则已存在",
|
"EXISTING_RULE": "规则已存在",
|
||||||
"ILLEGAL_RULE": "规则不合法",
|
"ILLEGAL_RULE": "规则不合法",
|
||||||
"INVALID_RULE": "无效规则",
|
"INVALID_RULE": "无效规则",
|
||||||
|
Loading…
Reference in New Issue
Block a user