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>
|
||||
</select>
|
||||
</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>
|
||||
<span class="refresh-btn" (click)="refresh()">
|
||||
<clr-icon shape="refresh"></clr-icon>
|
||||
|
@ -10,6 +10,10 @@ import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { ConfigurationService } from '../config/config.service';
|
||||
import { SessionService } from "../shared/session.service";
|
||||
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', () => {
|
||||
let component: 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(() => {
|
||||
TestBed.configureTestingModule({
|
||||
schemas: [
|
||||
@ -224,11 +241,16 @@ describe('ProjectComponent', () => {
|
||||
NoopAnimationsModule,
|
||||
HttpClientTestingModule
|
||||
],
|
||||
declarations: [ProjectComponent],
|
||||
declarations: [
|
||||
ProjectComponent,
|
||||
FilterComponent
|
||||
],
|
||||
providers: [
|
||||
TranslateService,
|
||||
{ provide: SessionService, useValue: mockSessionService },
|
||||
{ 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.
|
||||
// See the License for the specific language governing permissions and
|
||||
// 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 { ListProjectComponent } from './list-project/list-project.component';
|
||||
import { ProjectTypes } from '../shared/shared.const';
|
||||
import { ConfigurationService } from '../config/config.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 { 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({
|
||||
selector: 'project',
|
||||
templateUrl: 'project.component.html',
|
||||
styleUrls: ['./project.component.scss']
|
||||
})
|
||||
export class ProjectComponent implements OnInit {
|
||||
export class ProjectComponent implements OnInit, OnDestroy {
|
||||
projectTypes = ProjectTypes;
|
||||
quotaObj: QuotaHardInterface;
|
||||
@ViewChild(CreateProjectComponent, {static: false})
|
||||
@ -48,10 +54,15 @@ export class ProjectComponent implements OnInit {
|
||||
window.sessionStorage['projectTypeValue'] = +_project;
|
||||
}
|
||||
}
|
||||
@ViewChild(FilterComponent, {static: true})
|
||||
filterComponent: FilterComponent;
|
||||
searchSub: Subscription;
|
||||
|
||||
constructor(
|
||||
public configService: ConfigurationService,
|
||||
private session: SessionService
|
||||
private session: SessionService,
|
||||
private proService: ProjectService,
|
||||
private msgHandler: MessageHandlerService,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
@ -62,7 +73,47 @@ export class ProjectComponent implements OnInit {
|
||||
if (this.isSystemAdmin) {
|
||||
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() {
|
||||
this.configService.getConfiguration()
|
||||
.subscribe((configurations: Configuration) => {
|
||||
@ -72,6 +123,7 @@ export class ProjectComponent implements OnInit {
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
public get isSystemAdmin(): boolean {
|
||||
let account = this.session.getCurrentUser();
|
||||
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 {
|
||||
this.listProject.doFilterProject(+this.selecteType);
|
||||
}
|
||||
|
@ -37,6 +37,9 @@ const SCHEDULE_TYPE = {
|
||||
HOURLY: "Hourly",
|
||||
CUSTOM: "Custom"
|
||||
};
|
||||
const RUNNING: string = "Running";
|
||||
const PENDING: string = "pending";
|
||||
const TIMEOUT: number = 5000;
|
||||
@Component({
|
||||
selector: 'tag-retention',
|
||||
templateUrl: './tag-retention.component.html',
|
||||
@ -341,6 +344,14 @@ export class TagRetentionComponent implements OnInit {
|
||||
}
|
||||
this.historyList = response.body as Array<any>;
|
||||
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 => {
|
||||
this.errorHandler.error(error);
|
||||
});
|
||||
@ -362,6 +373,7 @@ export class TagRetentionComponent implements OnInit {
|
||||
this.tagRetentionService.getProjectInfo(this.projectId).subscribe(
|
||||
response => {
|
||||
this.retentionId = response.metadata.retention_id;
|
||||
this.refreshList();
|
||||
this.getRetention();
|
||||
}, error => {
|
||||
this.loadingRule = false;
|
||||
@ -435,6 +447,7 @@ export class TagRetentionComponent implements OnInit {
|
||||
return this.tagRetentionService.getI18nKey(str);
|
||||
}
|
||||
clrLoad() {
|
||||
|
||||
this.refreshList();
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
> .nav-item {
|
||||
> button {
|
||||
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;
|
||||
$fill-color1: #ccc;
|
||||
$right-status-fill-color: white;
|
||||
$light-color-green: #4cd400;
|
||||
|
||||
@import "./common.scss";
|
@ -20,4 +20,5 @@ $select-back-color: $mode-background-color;
|
||||
|
||||
$label-form-color: $mode-background-color3;
|
||||
$right-status-fill-color: #1d5100;
|
||||
$light-color-green: $right-status-fill-color;
|
||||
@import "./common.scss";
|
@ -1229,7 +1229,7 @@
|
||||
"RULE_TEMPLATE_6": "最近#天被拉取过的镜像",
|
||||
"RULE_TEMPLATE_7": "最近#天被推送过的镜像",
|
||||
"SCHEDULE": "定时任务",
|
||||
"SCHEDULE_WARNING": "执行保留策略会将会删除受影响的镜像,且不可恢复。请在制定定时任务前仔细检查所有保留规则。",
|
||||
"SCHEDULE_WARNING": "执行保留策略将会删除受影响的镜像,且不可恢复。请在制定定时任务前仔细检查所有保留规则。",
|
||||
"EXISTING_RULE": "规则已存在",
|
||||
"ILLEGAL_RULE": "规则不合法",
|
||||
"INVALID_RULE": "无效规则",
|
||||
|
Loading…
Reference in New Issue
Block a user