Improve tag-retention and project search function

Signed-off-by: sshijun <sshijun@vmware.com>

Improve tag-retention and project search function

Signed-off-by: sshijun <sshijun@vmware.com>
This commit is contained in:
sshijun 2020-01-13 17:32:56 +08:00
parent 75ad5c796a
commit 95287db4b5
8 changed files with 112 additions and 23 deletions

View File

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

View File

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

View File

@ -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,16 +73,57 @@ 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) => {
this.quotaObj = { this.quotaObj = {
count_per_project: configurations.count_per_project ? configurations.count_per_project.value : -1, count_per_project: configurations.count_per_project ? configurations.count_per_project.value : -1,
storage_per_project: configurations.storage_per_project ? configurations.storage_per_project.value : -1 storage_per_project: configurations.storage_per_project ? configurations.storage_per_project.value : -1
}; };
}); });
} }
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);
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -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": "无效规则",