diff --git a/src/portal/src/app/project/project.component.html b/src/portal/src/app/project/project.component.html index 0806cd057..91e51d7e2 100644 --- a/src/portal/src/app/project/project.component.html +++ b/src/portal/src/app/project/project.component.html @@ -15,7 +15,7 @@ - @@ -25,4 +25,4 @@ - \ No newline at end of file + diff --git a/src/portal/src/app/project/project.component.spec.ts b/src/portal/src/app/project/project.component.spec.ts index 03b3aeac0..5972949f6 100644 --- a/src/portal/src/app/project/project.component.spec.ts +++ b/src/portal/src/app/project/project.component.spec.ts @@ -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; @@ -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 }, ] }) diff --git a/src/portal/src/app/project/project.component.ts b/src/portal/src/app/project/project.component.ts index b8ed02ef1..a5daa00a4 100644 --- a/src/portal/src/app/project/project.component.ts +++ b/src/portal/src/app/project/project.component.ts @@ -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,16 +73,57 @@ 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) => { - this.quotaObj = { - 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 - }; - }); + .subscribe((configurations: Configuration) => { + this.quotaObj = { + 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 + }; + }); } + 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); } diff --git a/src/portal/src/app/project/tag-feature-integration/tag-retention/tag-retention.component.ts b/src/portal/src/app/project/tag-feature-integration/tag-retention/tag-retention.component.ts index 74ce80b65..1c04c38d5 100644 --- a/src/portal/src/app/project/tag-feature-integration/tag-retention/tag-retention.component.ts +++ b/src/portal/src/app/project/tag-feature-integration/tag-retention/tag-retention.component.ts @@ -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; 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(); } } diff --git a/src/portal/src/css/common.scss b/src/portal/src/css/common.scss index 24f620b5b..8154d244c 100644 --- a/src/portal/src/css/common.scss +++ b/src/portal/src/css/common.scss @@ -4,7 +4,7 @@ > .nav-item { > button { box-shadow: 0 -3px 0 $mode-link-color2 inset; - color: 0077b8; + color: #0077b8; } } } @@ -115,4 +115,9 @@ clr-dg-action-overflow { color: $select-option-color; } } -} \ No newline at end of file +} +hbr-tag { + .color-green { + color: $light-color-green !important; + } +} diff --git a/src/portal/src/css/dark-theme.scss b/src/portal/src/css/dark-theme.scss index 7a50d8101..c3f4e82e1 100644 --- a/src/portal/src/css/dark-theme.scss +++ b/src/portal/src/css/dark-theme.scss @@ -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"; \ No newline at end of file +@import "./common.scss"; diff --git a/src/portal/src/css/light-theme.scss b/src/portal/src/css/light-theme.scss index 380cc24b3..fa6e0e42b 100644 --- a/src/portal/src/css/light-theme.scss +++ b/src/portal/src/css/light-theme.scss @@ -20,4 +20,5 @@ $select-back-color: $mode-background-color; $label-form-color: $mode-background-color3; $right-status-fill-color: #1d5100; -@import "./common.scss"; \ No newline at end of file +$light-color-green: $right-status-fill-color; +@import "./common.scss"; diff --git a/src/portal/src/i18n/lang/zh-cn-lang.json b/src/portal/src/i18n/lang/zh-cn-lang.json index 266614b30..0487f1ee0 100644 --- a/src/portal/src/i18n/lang/zh-cn-lang.json +++ b/src/portal/src/i18n/lang/zh-cn-lang.json @@ -1229,7 +1229,7 @@ "RULE_TEMPLATE_6": "最近#天被拉取过的镜像", "RULE_TEMPLATE_7": "最近#天被推送过的镜像", "SCHEDULE": "定时任务", - "SCHEDULE_WARNING": "执行保留策略会将会删除受影响的镜像,且不可恢复。请在制定定时任务前仔细检查所有保留规则。", + "SCHEDULE_WARNING": "执行保留策略将会删除受影响的镜像,且不可恢复。请在制定定时任务前仔细检查所有保留规则。", "EXISTING_RULE": "规则已存在", "ILLEGAL_RULE": "规则不合法", "INVALID_RULE": "无效规则",