diff --git a/src/portal/lib/src/create-edit-endpoint/create-edit-endpoint.component.ts b/src/portal/lib/src/create-edit-endpoint/create-edit-endpoint.component.ts index aacf319ec..b02926711 100644 --- a/src/portal/lib/src/create-edit-endpoint/create-edit-endpoint.component.ts +++ b/src/portal/lib/src/create-edit-endpoint/create-edit-endpoint.component.ts @@ -31,8 +31,6 @@ import { InlineAlertComponent } from "../inline-alert/inline-alert.component"; import { Endpoint } from "../service/interface"; import { clone, compareValue, isEmptyObject } from "../utils"; - - const FAKE_PASSWORD = "rjGcfuRu"; @Component({ @@ -72,14 +70,17 @@ export class CreateEditEndpointComponent private errorHandler: ErrorHandler, private translateService: TranslateService, private ref: ChangeDetectorRef - ) { } + ) {} ngOnInit(): void { - this.endpointService.getAdapters().subscribe(adapters => { - this.adapterList = adapters || []; - }, error => { - this.errorHandler.error(error); - }); + this.endpointService.getAdapters().subscribe( + adapters => { + this.adapterList = adapters || []; + }, + error => { + this.errorHandler.error(error); + } + ); } public get isValid(): boolean { @@ -118,7 +119,7 @@ export class CreateEditEndpointComponent insecure: false, name: "", type: "harbor", - url: "", + url: "" }; } @@ -167,8 +168,8 @@ export class CreateEditEndpointComponent this.translateService .get("DESTINATION.TITLE_EDIT") .subscribe(res => (this.modalTitle = res)); - this.endpointService.getEndpoint(targetId) - .subscribe(target => { + this.endpointService.getEndpoint(targetId).subscribe( + target => { this.target = target; // Keep data cache this.initVal = clone(target); @@ -179,7 +180,9 @@ export class CreateEditEndpointComponent this.open(); this.controlEnabled = true; this.forceRefreshView(2000); - }, error => this.errorHandler.error(error)); + }, + error => this.errorHandler.error(error) + ); } else { this.endpointId = ""; this.translateService @@ -213,18 +216,20 @@ export class CreateEditEndpointComponent } this.testOngoing = true; - this.endpointService.pingEndpoint(payload) - .subscribe(response => { + this.endpointService.pingEndpoint(payload).subscribe( + response => { this.inlineAlert.showInlineSuccess({ message: "DESTINATION.TEST_CONNECTION_SUCCESS" }); this.forceRefreshView(2000); this.testOngoing = false; - }, error => { + }, + error => { this.inlineAlert.showInlineError("DESTINATION.TEST_CONNECTION_FAILURE"); this.forceRefreshView(2000); this.testOngoing = false; - }); + } + ); } onSubmit() { @@ -240,8 +245,8 @@ export class CreateEditEndpointComponent return; // Avoid duplicated submitting } this.onGoing = true; - this.endpointService.createEndpoint(this.target) - .subscribe(response => { + this.endpointService.createEndpoint(this.target).subscribe( + response => { this.translateService .get("DESTINATION.CREATED_SUCCESS") .subscribe(res => this.errorHandler.info(res)); @@ -249,14 +254,16 @@ export class CreateEditEndpointComponent this.onGoing = false; this.close(); this.forceRefreshView(2000); - }, error => { + }, + error => { this.onGoing = false; let errorMessageKey = this.handleErrorMessageKey(error.status); this.translateService.get(errorMessageKey).subscribe(res => { this.inlineAlert.showInlineError(res); }); this.forceRefreshView(2000); - }); + } + ); } updateEndpoint() { @@ -272,6 +279,7 @@ export class CreateEditEndpointComponent if (isEmptyObject(changes)) { return; } + let changekeys: { [key: string]: any } = Object.keys(changes); changekeys.forEach((key: string) => { @@ -283,8 +291,8 @@ export class CreateEditEndpointComponent } this.onGoing = true; - this.endpointService.updateEndpoint(this.target.id, payload) - .subscribe(response => { + this.endpointService.updateEndpoint(this.target.id, payload).subscribe( + response => { this.translateService .get("DESTINATION.UPDATED_SUCCESS") .subscribe(res => this.errorHandler.info(res)); @@ -292,14 +300,16 @@ export class CreateEditEndpointComponent this.close(); this.onGoing = false; this.forceRefreshView(2000); - }, error => { + }, + error => { let errorMessageKey = this.handleErrorMessageKey(error.status); this.translateService.get(errorMessageKey).subscribe(res => { this.inlineAlert.showInlineError(res); }); this.onGoing = false; this.forceRefreshView(2000); - }); + } + ); } handleErrorMessageKey(status: number): string { @@ -353,7 +363,6 @@ export class CreateEditEndpointComponent if (!compareValue(this.formValues, data)) { this.formValues = data; - this.inlineAlert.close(); } } } @@ -368,20 +377,36 @@ export class CreateEditEndpointComponent } for (let prop of Object.keys(this.target)) { let field: any = this.initVal[prop]; - if (!compareValue(field, this.target[prop])) { - changes[prop] = this.target[prop]; - // Number - if (typeof field === "number") { - changes[prop] = +changes[prop]; - } + if (typeof field !== "object") { + if (!compareValue(field, this.target[prop])) { + changes[prop] = this.target[prop]; + // Number + if (typeof field === "number") { + changes[prop] = +changes[prop]; + } - // Trim string value - if (typeof field === "string") { - changes[prop] = ("" + changes[prop]).trim(); + // Trim string value + if (typeof field === "string") { + changes[prop] = ("" + changes[prop]).trim(); + } + } + } else { + for (let pro of Object.keys(field)) { + if (!compareValue(field[pro], this.target[prop][pro])) { + changes[pro] = this.target[prop][pro]; + // Number + if (typeof field[pro] === "number") { + changes[pro] = +changes[pro]; + } + + // Trim string value + if (typeof field[pro] === "string") { + changes[pro] = ("" + changes[pro]).trim(); + } + } } } } - return changes; } } diff --git a/src/portal/lib/src/list-replication-rule/list-replication-rule.component.html b/src/portal/lib/src/list-replication-rule/list-replication-rule.component.html index 6c18e20a8..9aa54ed0e 100644 --- a/src/portal/lib/src/list-replication-rule/list-replication-rule.component.html +++ b/src/portal/lib/src/list-replication-rule/list-replication-rule.component.html @@ -6,7 +6,8 @@ {{'REPLICATION.DELETE_POLICY' | translate}} {{'REPLICATION.REPLICATE' | translate}} - {{'REPLICATION.NAME' | translate}} + {{'REPLICATION.NAME' | translate}} + {{'REPLICATION.STATUS' | translate}} {{'REPLICATION.SRC_NAMESPACE' | translate}} {{'REPLICATION.REPLICATION_MODE' | translate}} {{'REPLICATION.DESTINATION_NAMESPACE' | translate}} @@ -15,6 +16,17 @@ {{'REPLICATION.PLACEHOLDER' | translate }} {{p.name}} + + + + Disabled + + {{'REPLICATION.RULE_DISABLED' | translate}} + + + Enabled + + {{p.src_registry ? p.src_registry.name : ''}} : {{p.src_namespaces?.length>0 ? p.src_namespaces[0]: ''}} @@ -28,7 +40,7 @@ {{p.src_registry && p.src_registry.id > 0 ? 'pull-based' : 'push-based'}} - {{p.dest_registry ? p.dest_registry.name : ''}} : {{p.dest_namespace? p.dest_namespace: ''}} + {{p.dest_registry ? p.dest_registry.name : ''}} : {{p.dest_namespace? p.dest_namespace: '-'}} {{p.trigger ? p.trigger.type : ''}} diff --git a/src/portal/lib/src/list-replication-rule/list-replication-rule.component.scss b/src/portal/lib/src/list-replication-rule/list-replication-rule.component.scss index df6c5371f..3ca201456 100644 --- a/src/portal/lib/src/list-replication-rule/list-replication-rule.component.scss +++ b/src/portal/lib/src/list-replication-rule/list-replication-rule.component.scss @@ -8,3 +8,6 @@ .min-width { width: 224px; } +.status-width { + width: 105px; +} diff --git a/src/portal/lib/src/replication/replication-tasks/replication-tasks.component.html b/src/portal/lib/src/replication/replication-tasks/replication-tasks.component.html index 427e9241f..bb8c6b511 100644 --- a/src/portal/lib/src/replication/replication-tasks/replication-tasks.component.html +++ b/src/portal/lib/src/replication/replication-tasks/replication-tasks.component.html @@ -69,22 +69,21 @@ Tasks - + - {{'REPLICATION.RESOURCE_TYPE' |translate}} - {{'REPLICATION.RESOURCE' | translate}} - {{'REPLICATION.DESTINATION' | translate}} + {{'REPLICATION.RESOURCE_TYPE' |translate}} {{'REPLICATION.STATUS' | translate}} + filterPlaceholder='{{"REPLICATION.FILTER_PLACEHOLDER" | translate}}' + (filterEvt)="doSearch($event)" [currentValue]="searchTask"> - + {{'REPLICATION.TASK_ID'| translate}} {{'REPLICATION.RESOURCE_TYPE' | translate}} @@ -92,6 +91,8 @@ translate}} {{'REPLICATION.DESTINATION' | translate}} + {{'REPLICATION.OPERATION' | + translate}} {{'REPLICATION.STATUS' | translate}} {{'REPLICATION.CREATION_TIME' @@ -104,6 +105,7 @@ {{t.resource_type}} {{t.src_resource}} {{t.dst_resource}} + {{t.operation}} {{t.status}} {{t.start_time | date: 'short'}} {{t.end_time | date: 'short'}} @@ -123,7 +125,7 @@ {{pagination.lastItem +1 }} {{'ROBOT_ACCOUNT.OF' | translate}} {{pagination.totalItems }} {{'ROBOT_ACCOUNT.ITEMS' | translate}} - + diff --git a/src/portal/lib/src/replication/replication-tasks/replication-tasks.component.scss b/src/portal/lib/src/replication/replication-tasks/replication-tasks.component.scss index 3c3af43e0..abfa879a4 100644 --- a/src/portal/lib/src/replication/replication-tasks/replication-tasks.component.scss +++ b/src/portal/lib/src/replication/replication-tasks/replication-tasks.component.scss @@ -10,7 +10,7 @@ display: flex; align-items: center; >div:first-child { - width: 210px; + width: 250px; } >div:nth-child(2) { width: 140px; @@ -72,7 +72,7 @@ display: flex; justify-content: flex-end; - .filterTag { + .filter-tag { float: left; margin-top: 8px; } @@ -85,5 +85,8 @@ color: #007CBB; } } + clr-datagrid { + margin-top: 20px; + } } } diff --git a/src/portal/lib/src/replication/replication-tasks/replication-tasks.component.ts b/src/portal/lib/src/replication/replication-tasks/replication-tasks.component.ts index 43daa09ad..d74eb1223 100644 --- a/src/portal/lib/src/replication/replication-tasks/replication-tasks.component.ts +++ b/src/portal/lib/src/replication/replication-tasks/replication-tasks.component.ts @@ -4,8 +4,9 @@ import { ReplicationService } from "../../service/replication.service"; import { TranslateService } from '@ngx-translate/core'; import { finalize } from "rxjs/operators"; import { ErrorHandler } from "../../error-handler/error-handler"; -import { ReplicationJob, ReplicationTasks, Comparator, ReplicationJobItem } from "../../service/interface"; -import { CustomComparator } from "../../utils"; +import { ReplicationJob, ReplicationTasks, Comparator, ReplicationJobItem, State } from "../../service/interface"; +import { CustomComparator, DEFAULT_PAGE_SIZE, calculatePage, doFiltering, doSorting } from "../../utils"; +import { RequestQueryParams } from "../../service/RequestQueryParams"; @Component({ selector: 'replication-tasks', templateUrl: './replication-tasks.component.html', @@ -14,10 +15,16 @@ import { CustomComparator } from "../../utils"; export class ReplicationTasksComponent implements OnInit { isOpenFilterTag: boolean; selectedRow: []; - loading = false; + currentPage: number = 1; + currentPagePvt: number = 0; + totalCount: number = 0; + pageSize: number = DEFAULT_PAGE_SIZE; + currentState: State; + loading = true; searchTask: string; - defaultFilter = "resourceType"; - tasks: ReplicationTasks[] = []; + defaultFilter = "resource_type"; + tasks: ReplicationTasks; + taskItem: ReplicationTasks[] = []; tasksCopy: ReplicationTasks[] = []; stopOnGoing: boolean; executions: ReplicationJobItem[]; @@ -37,7 +44,6 @@ export class ReplicationTasksComponent implements OnInit { ) { } ngOnInit(): void { - this.clrLoadTasks(); this.searchTask = ''; this.getExecutionDetail(); } @@ -97,34 +103,49 @@ export class ReplicationTasksComponent implements OnInit { return this.replicationService.getJobBaseUrl() + "/executions/" + this.executionId + "/tasks/" + taskId + "/log"; } - clrLoadTasks(): void { - this.loading = true; - this.replicationService.getReplicationTasks(this.executionId) - .pipe(finalize(() => (this.loading = false))) - .subscribe(tasks => { - if (this.defaultFilter === 'resourceType') { - this.tasks = tasks.filter(x => - x.resource_type.includes(this.searchTask) - ); - } else if (this.defaultFilter === 'resource') { - this.tasks = tasks.filter(x => - x.src_resource.includes(this.searchTask) - ); - } else if (this.defaultFilter === 'destination') { - this.tasks = tasks.filter(x => - x.dst_resource.includes(this.searchTask) - ); - } else { - this.tasks = tasks.filter(x => - x.status.includes(this.searchTask) - ); + clrLoadTasks(state: State): void { + + if (!state || !state.page) { + return; + } + // Keep it for future filter + this.currentState = state; + + let pageNumber: number = calculatePage(state); + if (pageNumber !== this.currentPagePvt) { + // load data + let params: RequestQueryParams = new RequestQueryParams(); + params.set("page", '' + pageNumber); + params.set("page_size", '' + this.pageSize); + if (this.searchTask && this.searchTask !== "") { + params.set(this.defaultFilter, this.searchTask); } - this.tasksCopy = tasks.map(x => Object.assign({}, x)); + this.loading = true; + this.replicationService.getReplicationTasks(this.executionId, params) + .pipe(finalize(() => (this.loading = false))) + .subscribe(res => { + this.totalCount = res.length; + this.tasks = res; // Keep the data + this.taskItem = this.tasks.filter(tasks => tasks.resource_type !== ""); + this.taskItem = doFiltering(this.taskItem, state); + + this.taskItem = doSorting(this.taskItem, state); + + this.currentPagePvt = pageNumber; }, error => { this.errorHandler.error(error); }); + } else { + + this.taskItem = this.tasks.filter(tasks => tasks.resource_type !== ""); + // Do customized filter + this.taskItem = doFiltering(this.taskItem, state); + + // Do customized sorting + this.taskItem = doSorting(this.taskItem, state); + } } onBack(): void { this.router.navigate(["harbor", "replications"]); @@ -138,15 +159,41 @@ export class ReplicationTasksComponent implements OnInit { // refresh icon refreshTasks(): void { this.searchTask = ''; - this.clrLoadTasks(); + this.loading = true; + this.replicationService.getReplicationTasks(this.executionId) + .subscribe(res => { + this.tasks = res; + this.loading = false; + }, + error => { + this.loading = false; + this.errorHandler.error(error); + }); } - doSearch(value: string): void { + public doSearch(value: string): void { if (!value) { return; } this.searchTask = value.trim(); - this.clrLoadTasks(); + this.loading = true; + this.currentPage = 1; + if (this.currentPagePvt === 1) { + // Force reloading + let st: State = this.currentState; + if (!st) { + st = { + page: {} + }; + } + st.page.from = 0; + st.page.to = this.pageSize - 1; + st.page.size = this.pageSize; + + this.currentPagePvt = 0; + + this.clrLoadTasks(st); + } } openFilter(isOpen: boolean): void { diff --git a/src/portal/lib/src/replication/replication.component.html b/src/portal/lib/src/replication/replication.component.html index af25d2161..ec7601c68 100644 --- a/src/portal/lib/src/replication/replication.component.html +++ b/src/portal/lib/src/replication/replication.component.html @@ -24,19 +24,21 @@ {{'REPLICATION.REPLICATION_EXECUTIONS' | translate}} - - {{"REPLICATION.ADVANCED" | translate}} - - - - - - {{j.description | translate}} - - - - - + + + + + {{'REPLICATION.REPLICATION_TRIGGER' |translate}} + {{'REPLICATION.STATUS' | translate}} + + + + + + + @@ -61,11 +63,19 @@ {{j.trigger}} {{j.start_time | date: 'short'}} - {{'3mins'}} + {{getDuration(j)}} - {{'90%'}} + {{(j.succeed > 0 ? j.succeed / j.total : 0) | percent }} + + + {{j.status}} + + + + {{j.status_text}} + + - {{j.status}} {{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'REPLICATION.OF' | translate}} diff --git a/src/portal/lib/src/replication/replication.component.scss b/src/portal/lib/src/replication/replication.component.scss index 75448d29b..7ccf52090 100644 --- a/src/portal/lib/src/replication/replication.component.scss +++ b/src/portal/lib/src/replication/replication.component.scss @@ -13,6 +13,25 @@ padding-right: 16px; } +.execution-select { + padding-right: 18px; + height: 24px; + display: flex; + justify-content: flex-end; + padding-top: 25px; + .filter-tag { + float: left; + margin-top: 8px; + } + .refresh-btn { + cursor: pointer; + margin-top: 7px; + &:hover { + color: #007CBB; + } + } +} + .rightPos{ position: absolute; right: 35px; @@ -48,4 +67,10 @@ margin-top: 24px; } } +} + +clr-datagrid { + ::ng-deep .datagrid-table { + position: static; + } } \ No newline at end of file diff --git a/src/portal/lib/src/replication/replication.component.spec.ts b/src/portal/lib/src/replication/replication.component.spec.ts index 2ceffb373..fde985608 100644 --- a/src/portal/lib/src/replication/replication.component.spec.ts +++ b/src/portal/lib/src/replication/replication.component.spec.ts @@ -262,7 +262,6 @@ describe('Replication Component (inline template)', () => { fixture.detectChanges(); fixture.whenStable().then(() => { fixture.detectChanges(); - comp.doFilterJobStatus('finished'); let el: HTMLElement = deJobs.nativeElement; fixture.detectChanges(); expect(el).toBeTruthy(); @@ -274,8 +273,6 @@ describe('Replication Component (inline template)', () => { fixture.detectChanges(); fixture.whenStable().then(() => { fixture.detectChanges(); - comp.doJobSearchByStartTime('2017-05-01'); - comp.doJobSearchByEndTime('2015-05-25'); let el: HTMLElement = deJobs.nativeElement; fixture.detectChanges(); expect(el).toBeTruthy(); diff --git a/src/portal/lib/src/replication/replication.component.ts b/src/portal/lib/src/replication/replication.component.ts index 731031be8..3c245a164 100644 --- a/src/portal/lib/src/replication/replication.component.ts +++ b/src/portal/lib/src/replication/replication.component.ts @@ -53,9 +53,16 @@ import { import { ConfirmationMessage } from "../confirmation-dialog/confirmation-message"; import { ConfirmationDialogComponent } from "../confirmation-dialog/confirmation-dialog.component"; import { ConfirmationAcknowledgement } from "../confirmation-dialog/confirmation-state-message"; -import { operateChanges, OperationState, OperateInfo } from "../operation/operate"; +import { + operateChanges, + OperationState, + OperateInfo +} from "../operation/operate"; import { OperationService } from "../operation/operation.service"; import { Router } from "@angular/router"; +const ONE_HOUR_SECONDS: number = 3600; +const ONE_MINUTE_SECONDS: number = 60; +const ONE_DAY_SECONDS: number = 24 * ONE_HOUR_SECONDS; const ruleStatus: { [key: string]: any } = [ { key: "all", description: "REPLICATION.ALL_STATUS" }, @@ -63,26 +70,11 @@ const ruleStatus: { [key: string]: any } = [ { key: "0", description: "REPLICATION.DISABLED" } ]; -const jobStatus: { [key: string]: any } = [ - { key: "all", description: "REPLICATION.ALL" }, - { key: "pending", description: "REPLICATION.PENDING" }, - { key: "running", description: "REPLICATION.RUNNING" }, - { key: "error", description: "REPLICATION.ERROR" }, - { key: "retrying", description: "REPLICATION.RETRYING" }, - { key: "stopped", description: "REPLICATION.STOPPED" }, - { key: "finished", description: "REPLICATION.FINISHED" }, - { key: "canceled", description: "REPLICATION.CANCELED" } -]; - export class SearchOption { ruleId: number | string; ruleName: string = ""; - repoName: string = ""; + trigger: string = ""; status: string = ""; - startTime: string = ""; - startTimestamp: string = ""; - endTime: string = ""; - endTimestamp: string = ""; page: number = 1; pageSize: number = DEFAULT_PAGE_SIZE; } @@ -109,12 +101,12 @@ export class ReplicationComponent implements OnInit, OnDestroy { @Output() goToRegistry = new EventEmitter(); search: SearchOption = new SearchOption(); - + isOpenFilterTag: boolean; ruleStatus = ruleStatus; currentRuleStatus: { key: string; description: string }; - jobStatus = jobStatus; - currentJobStatus: { key: string; description: string }; + currentTerm: string; + defaultFilter = "trigger"; changedRules: ReplicationRule[]; @@ -125,7 +117,6 @@ export class ReplicationComponent implements OnInit, OnDestroy { hiddenJobList = true; jobs: ReplicationJobItem[]; - currentJobSearchOption: number; @ViewChild(ListReplicationRuleComponent) listReplicationRule: ListReplicationRuleComponent; @@ -133,7 +124,6 @@ export class ReplicationComponent implements OnInit, OnDestroy { @ViewChild(CreateEditRuleComponent) createEditPolicyComponent: CreateEditRuleComponent; - @ViewChild("replicationConfirmDialog") replicationConfirmDialog: ConfirmationDialogComponent; @@ -142,10 +132,10 @@ export class ReplicationComponent implements OnInit, OnDestroy { creationTimeComparator: Comparator = new CustomComparator< ReplicationJob - >("start_time", "date"); + >("start_time", "date"); updateTimeComparator: Comparator = new CustomComparator< ReplicationJob - >("end_time", "date"); + >("end_time", "date"); // Server driven pagination currentPage: number = 1; @@ -160,7 +150,8 @@ export class ReplicationComponent implements OnInit, OnDestroy { private errorHandler: ErrorHandler, private replicationService: ReplicationService, private operationService: OperationService, - private translateService: TranslateService) { } + private translateService: TranslateService + ) {} public get showPaginationIndex(): boolean { return this.totalCount > 0; @@ -168,8 +159,6 @@ export class ReplicationComponent implements OnInit, OnDestroy { ngOnInit() { this.currentRuleStatus = this.ruleStatus[0]; - this.currentJobStatus = this.jobStatus[0]; - this.currentJobSearchOption = 0; } ngOnDestroy() { @@ -215,43 +204,23 @@ export class ReplicationComponent implements OnInit, OnDestroy { // Pagination params.set("page", "" + pageNumber); params.set("page_size", "" + this.pageSize); - // Search by status - if (this.search.status.trim()) { - params.set("status", this.search.status); - } - // Search by repository - if (this.search.repoName.trim()) { - params.set("repository", this.search.repoName); - } - // Search by timestamps - if (this.search.startTimestamp.trim()) { - params.set("start_time", this.search.startTimestamp); - } - if (this.search.endTimestamp.trim()) { - params.set("end_time", this.search.endTimestamp); + + if (this.currentTerm && this.currentTerm !== "") { + params.set(this.defaultFilter, this.currentTerm); } this.jobsLoading = true; - // Do filtering and sorting - this.jobs = doFiltering(this.jobs, state); - this.jobs = doSorting(this.jobs, state); - - this.jobsLoading = false; - - this.replicationService.getExecutions(this.search.ruleId, params) - .subscribe(response => { + this.replicationService.getExecutions(this.search.ruleId, params).subscribe( + response => { this.totalCount = response.metadata.xTotalCount; this.jobs = response.data; - if (!this.timerDelay) { this.timerDelay = timer(10000, 10000).subscribe(() => { let count: number = 0; this.jobs.forEach(job => { if ( - job.status === "pending" || - job.status === "running" || - job.status === "retrying" + job.status === "InProgress" ) { count++; } @@ -264,18 +233,30 @@ export class ReplicationComponent implements OnInit, OnDestroy { } }); } - // Do filtering and sorting this.jobs = doFiltering(this.jobs, state); this.jobs = doSorting(this.jobs, state); this.jobsLoading = false; - }, error => { + }, + error => { this.jobsLoading = false; this.errorHandler.error(error); - }); + } + ); + } + public doSearchExecutions(terms: string): void { + if (!terms) { + return; + } + this.currentTerm = terms.trim(); + // Trigger data loading and start from first page + this.jobsLoading = true; + this.currentPage = 1; + this.jobsLoading = true; + // Force reloading + this.loadFirstPage(); } - loadFirstPage(): void { let st: State = this.currentState; if (!st) { @@ -294,10 +275,6 @@ export class ReplicationComponent implements OnInit, OnDestroy { if (rule && rule.id) { this.hiddenJobList = false; this.search.ruleId = rule.id || ""; - this.search.repoName = ""; - this.search.status = ""; - this.currentJobSearchOption = 0; - this.currentJobStatus = { key: "all", description: "REPLICATION.ALL" }; this.loadFirstPage(); } } @@ -325,7 +302,7 @@ export class ReplicationComponent implements OnInit, OnDestroy { let rule: ReplicationRule = message.data; if (rule) { - forkJoin(this.replicationOperate(rule)).subscribe((item) => { + forkJoin(this.replicationOperate(rule)).subscribe(item => { this.selectOneRule(rule); }); } @@ -335,30 +312,39 @@ export class ReplicationComponent implements OnInit, OnDestroy { replicationOperate(rule: ReplicationRule): Observable { // init operation info let operMessage = new OperateInfo(); - operMessage.name = 'OPERATION.REPLICATION'; + operMessage.name = "OPERATION.REPLICATION"; operMessage.data.id = rule.id; operMessage.state = OperationState.progressing; operMessage.data.name = rule.name; this.operationService.publishInfo(operMessage); - return this.replicationService.replicateRule(+rule.id) - .pipe(map(response => { - this.translateService.get('BATCH.REPLICATE_SUCCESS') - .subscribe(res => operateChanges(operMessage, OperationState.success)); - }) - , catchError(error => { - if (error && error.status === 412) { - return forkJoin(this.translateService.get('BATCH.REPLICATE_FAILURE'), - this.translateService.get('REPLICATION.REPLICATE_SUMMARY_FAILURE')) - .pipe(map(function (res) { - operateChanges(operMessage, OperationState.failure, res[1]); - })); - } else { - return this.translateService.get('BATCH.REPLICATE_FAILURE').pipe(map(res => { + return this.replicationService.replicateRule(+rule.id).pipe( + map(response => { + this.translateService + .get("BATCH.REPLICATE_SUCCESS") + .subscribe(res => + operateChanges(operMessage, OperationState.success) + ); + }), + catchError(error => { + if (error && error.status === 412) { + return forkJoin( + this.translateService.get("BATCH.REPLICATE_FAILURE"), + this.translateService.get("REPLICATION.REPLICATE_SUMMARY_FAILURE") + ).pipe( + map(function(res) { + operateChanges(operMessage, OperationState.failure, res[1]); + }) + ); + } else { + return this.translateService.get("BATCH.REPLICATE_FAILURE").pipe( + map(res => { operateChanges(operMessage, OperationState.failure, res); - })); - } - })); + }) + ); + } + }) + ); } customRedirect(rule: ReplicationRule) { @@ -370,21 +356,17 @@ export class ReplicationComponent implements OnInit, OnDestroy { this.listReplicationRule.retrieveRules(ruleName); } - doFilterJobStatus($event: any) { - if ($event && $event.target && $event.target["value"]) { - let status = $event.target["value"]; - - this.currentJobStatus = this.jobStatus.find((r: any) => r.key === status); - if (this.currentJobStatus.key === "all") { - status = ""; - } - this.search.status = status; - this.doSearchJobs(this.search.repoName); - } + doFilterJob($event: any): void { + this.defaultFilter = $event["target"].value; + this.doSearchJobs(this.currentTerm); } - doSearchJobs(repoName: string) { - this.search.repoName = repoName; + doSearchJobs(terms: string) { + if (!terms) { + return; + } + this.currentTerm = terms.trim(); + this.currentPage = 1; this.loadFirstPage(); } @@ -434,7 +416,7 @@ export class ReplicationComponent implements OnInit, OnDestroy { this.selectedRow = []; }) ) - .subscribe(() => { }); + .subscribe(() => {}); } } @@ -468,14 +450,7 @@ export class ReplicationComponent implements OnInit, OnDestroy { } refreshJobs() { - this.currentJobStatus = this.jobStatus[0]; - this.search.startTime = " "; - this.search.endTime = " "; - this.search.repoName = ""; - this.search.startTimestamp = ""; - this.search.endTimestamp = ""; - this.search.status = ""; - + this.currentTerm = ""; this.currentPage = 1; let st: State = { @@ -488,19 +463,36 @@ export class ReplicationComponent implements OnInit, OnDestroy { this.clrLoadJobs(st); } - toggleSearchJobOptionalName(option: number) { - option === 1 - ? (this.currentJobSearchOption = 0) - : (this.currentJobSearchOption = 1); + openFilter(isOpen: boolean): void { + if (isOpen) { + this.isOpenFilterTag = true; + } else { + this.isOpenFilterTag = false; + } } + getDuration(j: ReplicationJobItem) { + if (!j) { + return; + } + if (j.status === "Failed") { + return "-"; + } + let start_time = new Date(j.start_time).getTime(); + let end_time = new Date(j.end_time).getTime(); + let timesDiff = end_time - start_time; + let timesDiffSeconds = timesDiff / 1000; + let minutes = Math.floor(((timesDiffSeconds % ONE_DAY_SECONDS) % ONE_HOUR_SECONDS) / ONE_MINUTE_SECONDS); + let seconds = Math.floor(timesDiffSeconds % ONE_MINUTE_SECONDS); + if (minutes > 0) { + return minutes + "m" + seconds + "s"; + } - doJobSearchByStartTime(fromTimestamp: string) { - this.search.startTimestamp = fromTimestamp; - this.loadFirstPage(); - } + if (seconds > 0) { + return seconds + "s"; + } - doJobSearchByEndTime(toTimestamp: string) { - this.search.endTimestamp = toTimestamp; - this.loadFirstPage(); + if (seconds <= 0 && timesDiff > 0) { + return timesDiff + 'ms'; + } } } diff --git a/src/portal/lib/src/service/interface.ts b/src/portal/lib/src/service/interface.ts index 5d5c71b28..a3d1f0c70 100644 --- a/src/portal/lib/src/service/interface.ts +++ b/src/portal/lib/src/service/interface.ts @@ -175,6 +175,7 @@ export interface ReplicationJobItem extends Base { */ export interface ReplicationTasks extends Base { [key: string]: any | any[]; + operation: string; id: number; execution_id: number; resource_type: string; diff --git a/src/portal/lib/src/service/replication.service.ts b/src/portal/lib/src/service/replication.service.ts index 913e6c550..ce9d33760 100644 --- a/src/portal/lib/src/service/replication.service.ts +++ b/src/portal/lib/src/service/replication.service.ts @@ -67,7 +67,8 @@ export abstract class ReplicationService { * @memberOf ReplicationService */ abstract getReplicationTasks( - executionId: number | string + executionId: number | string, + queryParams?: RequestQueryParams ): Observable; /** * Create new replication rule. @@ -287,14 +288,16 @@ export class ReplicationDefaultService extends ReplicationService { } public getReplicationTasks( - executionId: number | string + executionId: number | string, + queryParams?: RequestQueryParams ): Observable { if (!executionId) { return observableThrowError("Bad argument"); } let url: string = `${this._replicateUrl}/executions/${executionId}/tasks`; return this.http - .get(url, HTTP_GET_OPTIONS) + .get(url, + queryParams ? buildHttpRequestOptions(queryParams) : HTTP_GET_OPTIONS) .pipe(map(response => response.json() as ReplicationTasks) , catchError(error => observableThrowError(error))); } diff --git a/src/portal/src/i18n/lang/en-us-lang.json b/src/portal/src/i18n/lang/en-us-lang.json index 9b8fbc58d..78b221192 100644 --- a/src/portal/src/i18n/lang/en-us-lang.json +++ b/src/portal/src/i18n/lang/en-us-lang.json @@ -340,6 +340,7 @@ "OF": "of" }, "REPLICATION": { + "OPERATION": "Operation", "CURRENT": "current", "FILTER_PLACEHOLDER": "Filter Tasks", "STOP_TITLE": "Confirm Stop Executions",