diff --git a/demo-shell/src/app/components/process-service/process-service.component.html b/demo-shell/src/app/components/process-service/process-service.component.html index bd82e5e4da..aeaaee51b8 100644 --- a/demo-shell/src/app/components/process-service/process-service.component.html +++ b/demo-shell/src/app/components/process-service/process-service.component.html @@ -133,11 +133,14 @@
--> + +
('adf-start-process.name'); this.defaultProcessDefinitionName = this.appConfig.get('adf-start-process.processDefinitionName'); @@ -202,11 +208,26 @@ export class ProcessServiceComponent implements AfterViewInit, OnDestroy, OnInit this.taskPage--; } + onPrevPageProcess(pagination: Pagination): void { + this.processPage = this.processListPagination.current - 1; + } + onNextPage(pagination: Pagination): void { this.taskPagination.skipCount = pagination.skipCount; this.taskPage++; } + onNextPageProcess(pagination: Pagination): void { + this.processPage = this.processListPagination.current - 1; + } + + onChangePageSizeProcess(pagination: Pagination): void { + const { maxItems } = pagination; + this.preferenceService.paginationSize = maxItems; + this.processPage = this.processListPagination.current - 1; + this.paginationPageSize = maxItems; + } + onChangePageSize(pagination: Pagination): void { const { skipCount, maxItems } = pagination; this.taskPage = this.currentPage(skipCount, maxItems); @@ -222,6 +243,10 @@ export class ProcessServiceComponent implements AfterViewInit, OnDestroy, OnInit this.taskPagination.skipCount = skipCount; } + onChangePageNumberProcess(pagination: Pagination): void { + this.processPage = this.processListPagination.current - 1; + } + currentPage(skipCount: number, maxItems: number): number { return (skipCount && maxItems) ? Math.floor(skipCount / maxItems) : 0; } @@ -269,6 +294,12 @@ export class ProcessServiceComponent implements AfterViewInit, OnDestroy, OnInit onTaskFilterClick(filter: FilterRepresentationModel): void { this.applyTaskFilter(filter); + this.resetTaskPaginationPage(); + } + + resetTaskPaginationPage() { + this.taskPage = 0; + this.taskPagination.skipCount = 0; } onReportClick(event: any): void { @@ -302,6 +333,11 @@ export class ProcessServiceComponent implements AfterViewInit, OnDestroy, OnInit onProcessFilterClick(event: ProcessInstanceFilterRepresentation): void { this.processFilter = event; + this.resetProcessPaginationPage(); + } + + resetProcessPaginationPage() { + this.processPage = 0; } onSuccessProcessFilterList(event: ProcessInstanceFilterRepresentation[]): void { diff --git a/docs/process-list.component.md b/docs/process-list.component.md index 51f7b86e79..af86557381 100644 --- a/docs/process-list.component.md +++ b/docs/process-list.component.md @@ -55,6 +55,8 @@ define custom schema in the app.config.json as shown below json format. | presetColumn | string | | The presetColumn of the custom schema to fetch. | | state | Define state of the processes. Possible values are `running`, `completed` and `all` | | | | sort | Define sort of the processes. Possible values are `created-desc`, `created-asc`, `ended-desc`, `ended-asc` | | | +| page | number | 0 | The page of the tasks to fetch. | +| size | number | 25 | The number of tasks to fetch. | | schemaColumn | List of columns to display in the process instances datatable (see the [Details](#details) section below) | | | ### Events diff --git a/docs/task-list.component.md b/docs/task-list.component.md index 368c2ec4f7..fe2fa5a6f0 100644 --- a/docs/task-list.component.md +++ b/docs/task-list.component.md @@ -134,7 +134,7 @@ You can also use both HTML-based and app.config.json custom schema declaration a | processInstanceId | string | | The processInstanceId of the process. | | presetColumn | string | | The presetColumn of the custom schema to fetch. | | page | number | 0 | The page of the tasks to fetch. | -| size | number | 5 | The number of tasks to fetch. | +| size | number | 25 | The number of tasks to fetch. | | assignment | string | | The assignment of the process.
    Possible values are:
  • assignee : where the current user is the assignee
  • candidate: where the current user is a task candidate
  • group_x: where the task is assigned to a group where the current user is a member of.
  • no value: where the current user is involved
| | selectionMode | string | 'single' | Row selection mode. Can be none, `single` or `multiple`. For `multiple` mode you can use Cmd (macOS) or Ctrl (Win) modifier key to toggle selection for multiple rows. | | multiselect | boolean | false | Toggles multiple row selection, renders checkboxes at the beginning of each row | diff --git a/lib/core/pagination/pagination.component.spec.ts b/lib/core/pagination/pagination.component.spec.ts index 24c4819f4d..50320a1e1a 100644 --- a/lib/core/pagination/pagination.component.spec.ts +++ b/lib/core/pagination/pagination.component.spec.ts @@ -169,35 +169,43 @@ describe('PaginationComponent', () => { }); it('goes next', () => { + expect(component.current).toBe(3); component.goNext(); const { skipCount } = nextPageSpy.calls.mostRecent().args[0]; expect(skipCount).toBe(75); + expect(component.current).toBe(4); }); it('goes previous', () => { + expect(component.current).toBe(3); component.goPrevious(); const { skipCount } = prevPageSpy.calls.mostRecent().args[0]; expect(skipCount).toBe(25); + expect(component.current).toBe(2); }); it('changes page size', () => { + expect(component.current).toBe(3); component.onChangePageSize(50); const { maxItems } = changePageSizeSpy.calls.mostRecent().args[0]; expect(maxItems).toBe(50); + expect(component.current).toBe(1); }); it('changes page number', () => { + expect(component.current).toBe(3); component.onChangePageNumber(5); const { skipCount } = changePageNumberSpy.calls.mostRecent().args[0]; expect(skipCount).toBe(100); + expect(component.current).toBe(5); }); }); @@ -318,10 +326,12 @@ describe('PaginationComponent', () => { component.target = customComponent; component.ngOnInit(); - customComponent.pagination.next(new FakePaginationInput(2, 0, 25)); + customComponent.pagination.next(new FakePaginationInput(2, 1, 25)); + expect(component.current).toBe(1); component.goNext(); expect(customComponent.updatePagination).toHaveBeenCalled(); + expect(component.current).toBe(2); }); }); diff --git a/lib/core/pagination/pagination.component.ts b/lib/core/pagination/pagination.component.ts index 75c32c1f33..0e60e698dc 100644 --- a/lib/core/pagination/pagination.component.ts +++ b/lib/core/pagination/pagination.component.ts @@ -168,6 +168,7 @@ export class PaginationComponent implements OnInit, OnDestroy { if (this.hasItems) { const maxItems = this.pagination.maxItems; const skipCount = (this.next - 1) * maxItems; + this.pagination.skipCount = skipCount; this.handlePaginationEvent(PaginationComponent.ACTIONS.NEXT_PAGE, { skipCount, @@ -180,6 +181,7 @@ export class PaginationComponent implements OnInit, OnDestroy { if (this.hasItems) { const maxItems = this.pagination.maxItems; const skipCount = (this.previous - 1) * maxItems; + this.pagination.skipCount = skipCount; this.handlePaginationEvent(PaginationComponent.ACTIONS.PREV_PAGE, { skipCount, @@ -192,6 +194,7 @@ export class PaginationComponent implements OnInit, OnDestroy { if (this.hasItems) { const maxItems = this.pagination.maxItems; const skipCount = (pageNumber - 1) * maxItems; + this.pagination.skipCount = skipCount; this.handlePaginationEvent(PaginationComponent.ACTIONS.CHANGE_PAGE_NUMBER, { skipCount, @@ -201,6 +204,8 @@ export class PaginationComponent implements OnInit, OnDestroy { } onChangePageSize(maxItems: number) { + this.pagination.skipCount = 0; + this.pagination.maxItems = maxItems; this.handlePaginationEvent(PaginationComponent.ACTIONS.CHANGE_PAGE_SIZE, { skipCount: 0, maxItems diff --git a/lib/process-services/attachment/process-attachment-list.component.spec.ts b/lib/process-services/attachment/process-attachment-list.component.spec.ts index 348c3e2c74..a13283b6d7 100644 --- a/lib/process-services/attachment/process-attachment-list.component.spec.ts +++ b/lib/process-services/attachment/process-attachment-list.component.spec.ts @@ -61,48 +61,48 @@ describe('ProcessAttachmentListComponent', () => { }); mockAttachment = { - 'size': 2, - 'total': 2, - 'start': 0, - 'data': [{ - 'id': 4001, - 'name': 'Invoice01.pdf', - 'created': '2017-05-12T12:50:05.522+0000', - 'createdBy': { - 'id': 1, - 'firstName': 'Apps', - 'lastName': 'Administrator', - 'email': 'admin@app.activiti.com', - 'company': 'Alfresco.com', - 'pictureId': 3003 + size: 2, + total: 2, + start: 0, + data: [{ + id: 4001, + name: 'Invoice01.pdf', + created: '2017-05-12T12:50:05.522+0000', + createdBy: { + id: 1, + firstName: 'Apps', + lastName: 'Administrator', + email: 'admin@app.activiti.com', + company: 'Alfresco.com', + pictureId: 3003 }, - 'relatedContent': true, - 'contentAvailable': true, - 'link': false, - 'mimeType': 'application/pdf', - 'simpleType': 'pdf', - 'previewStatus': 'created', - 'thumbnailStatus': 'created' + relatedContent: true, + contentAvailable: true, + link: false, + mimeType: 'application/pdf', + simpleType: 'pdf', + previewStatus: 'created', + thumbnailStatus: 'created' }, { - 'id': 4002, - 'name': 'Invoice02.pdf', - 'created': '2017-05-12T12:50:05.522+0000', - 'createdBy': { - 'id': 1, - 'firstName': 'Apps', - 'lastName': 'Administrator', - 'email': 'admin@app.activiti.com', - 'company': 'Alfresco.com', - 'pictureId': 3003 + id: 4002, + name: 'Invoice02.pdf', + created: '2017-05-12T12:50:05.522+0000', + createdBy: { + id: 1, + firstName: 'Apps', + lastName: 'Administrator', + email: 'admin@app.activiti.com', + company: 'Alfresco.com', + pictureId: 3003 }, - 'relatedContent': true, - 'contentAvailable': true, - 'link': false, - 'mimeType': 'application/pdf', - 'simpleType': 'pdf', - 'previewStatus': 'created', - 'thumbnailStatus': 'created' + relatedContent: true, + contentAvailable: true, + link: false, + mimeType: 'application/pdf', + simpleType: 'pdf', + previewStatus: 'created', + thumbnailStatus: 'created' }] }; @@ -115,6 +115,14 @@ describe('ProcessAttachmentListComponent', () => { )); }); + afterEach(() => { + const overlayContainers = window.document.querySelectorAll('.cdk-overlay-container'); + + overlayContainers.forEach((overlayContainer) => { + overlayContainer.innerHTML = ''; + }); + }); + it('should load attachments when processInstanceId specified', () => { let change = new SimpleChange(null, '123', true); component.ngOnChanges({ 'processInstanceId': change }); @@ -154,7 +162,7 @@ describe('ProcessAttachmentListComponent', () => { }); })); - it('should display all actions if attachements are not read only', () => { + it('should display all actions if attachements are not read only', async(() => { let change = new SimpleChange(null, '123', true); component.ngOnChanges({ 'processInstanceId': change }); @@ -163,14 +171,15 @@ describe('ProcessAttachmentListComponent', () => { actionButton.click(); fixture.whenStable().then(() => { fixture.detectChanges(); - let actionMenu = fixture.debugElement.nativeElement.querySelectorAll('button.mat-menu-item').length; - expect(fixture.debugElement.nativeElement.querySelector('[data-automation-id="View"]')).not.toBeNull(); - expect(fixture.debugElement.nativeElement.querySelector('[data-automation-id="Remove"]')).not.toBeNull(); + let actionMenu = window.document.querySelectorAll('button.mat-menu-item').length; + expect(window.document.querySelector('[data-automation-id="ADF_PROCESS_LIST.MENU_ACTIONS.VIEW_CONTENT"]')).not.toBeNull(); + expect(window.document.querySelector('[data-automation-id="ADF_PROCESS_LIST.MENU_ACTIONS.REMOVE_CONTENT"]')).not.toBeNull(); + expect(window.document.querySelector('[data-automation-id="ADF_PROCESS_LIST.MENU_ACTIONS.DOWNLOAD_CONTENT"]')).not.toBeNull(); expect(actionMenu).toBe(3); }); - }); + })); - it('should not display remove action if attachments are read only', () => { + it('should not display remove action if attachments are read only', async(() => { let change = new SimpleChange(null, '123', true); component.ngOnChanges({ 'processInstanceId': change }); component.disabled = true; @@ -180,12 +189,13 @@ describe('ProcessAttachmentListComponent', () => { actionButton.click(); fixture.whenStable().then(() => { fixture.detectChanges(); - let actionMenu = fixture.debugElement.nativeElement.querySelectorAll('button.mat-menu-item').length; - expect(fixture.debugElement.nativeElement.querySelector('[data-automation-id="View"]')).not.toBeNull(); - expect(fixture.debugElement.nativeElement.querySelector('[data-automation-id="Remove"]')).toBeNull(); + let actionMenu = window.document.querySelectorAll('button.mat-menu-item').length; + expect(window.document.querySelector('[data-automation-id="ADF_PROCESS_LIST.MENU_ACTIONS.VIEW_CONTENT"]')).not.toBeNull(); + expect(window.document.querySelector('[data-automation-id="ADF_PROCESS_LIST.MENU_ACTIONS.DOWNLOAD_CONTENT"]')).not.toBeNull(); + expect(window.document.querySelector('[data-automation-id="ADF_PROCESS_LIST.MENU_ACTIONS.REMOVE_CONTENT"]')).toBeNull(); expect(actionMenu).toBe(2); }); - }); + })); it('should show the empty list component when the attachments list is empty', async(() => { getProcessRelatedContentSpy.and.returnValue(Observable.of({ @@ -325,12 +335,12 @@ describe('Custom CustomEmptyTemplateComponent', () => { fixture.detectChanges(); }); - it('should render the custom template', () => { + it('should render the custom template', async(() => { fixture.whenStable().then(() => { fixture.detectChanges(); let title: any = fixture.debugElement.queryAll(By.css('[adf-empty-list-header]')); expect(title.length).toBe(1); expect(title[0].nativeElement.innerText).toBe('Custom header'); }); - }); + })); }); diff --git a/lib/process-services/attachment/process-attachment-list.component.ts b/lib/process-services/attachment/process-attachment-list.component.ts index f35f04c382..2120d32fed 100644 --- a/lib/process-services/attachment/process-attachment-list.component.ts +++ b/lib/process-services/attachment/process-attachment-list.component.ts @@ -61,7 +61,7 @@ export class ProcessAttachmentListComponent implements OnChanges, AfterContentIn hasCustomTemplate: boolean = false; attachments: any[] = []; - isLoading: boolean = true; + isLoading: boolean = false; constructor(private activitiContentService: ProcessContentService, private contentService: ContentService, diff --git a/lib/process-services/attachment/task-attachment-list.component.spec.ts b/lib/process-services/attachment/task-attachment-list.component.spec.ts index 16fb136d84..2910e8d08d 100644 --- a/lib/process-services/attachment/task-attachment-list.component.spec.ts +++ b/lib/process-services/attachment/task-attachment-list.component.spec.ts @@ -99,6 +99,13 @@ describe('TaskAttachmentList', () => { getFileRawContentSpy = spyOn(service, 'getFileRawContent').and.returnValue(Observable.of(blobObj)); }); + afterEach(() => { + const overlayContainers = window.document.querySelectorAll('.cdk-overlay-container'); + overlayContainers.forEach((overlayContainer) => { + overlayContainer.innerHTML = ''; + }); + }); + it('should load attachments when taskId specified', () => { let change = new SimpleChange(null, '123', true); component.ngOnChanges({ 'taskId': change }); @@ -168,7 +175,7 @@ describe('TaskAttachmentList', () => { }); })); - it('should display all actions if attachments are not read only', () => { + it('should display all actions if attachments are not read only', async(() => { let change = new SimpleChange(null, '123', true); component.ngOnChanges({ 'taskId': change }); fixture.detectChanges(); @@ -177,14 +184,15 @@ describe('TaskAttachmentList', () => { actionButton.click(); fixture.whenStable().then(() => { fixture.detectChanges(); - let actionMenu = fixture.debugElement.nativeElement.querySelectorAll('button.mat-menu-item').length; - expect(fixture.debugElement.nativeElement.querySelector('[data-automation-id="View"]')).not.toBeNull(); - expect(fixture.debugElement.nativeElement.querySelector('[data-automation-id="Remove"]')).not.toBeNull(); + let actionMenu = window.document.querySelectorAll('button.mat-menu-item').length; + expect(window.document.querySelector('[data-automation-id="ADF_TASK_LIST.MENU_ACTIONS.VIEW_CONTENT"]')).not.toBeNull(); + expect(window.document.querySelector('[data-automation-id="ADF_TASK_LIST.MENU_ACTIONS.REMOVE_CONTENT"]')).not.toBeNull(); + expect(window.document.querySelector('[data-automation-id="ADF_TASK_LIST.MENU_ACTIONS.DOWNLOAD_CONTENT"]')).not.toBeNull(); expect(actionMenu).toBe(3); }); - }); + })); - it('should not display remove action if attachments are read only', () => { + it('should not display remove action if attachments are read only', async(() => { let change = new SimpleChange(null, '123', true); component.ngOnChanges({ 'taskId': change }); component.disabled = true; @@ -194,12 +202,13 @@ describe('TaskAttachmentList', () => { actionButton.click(); fixture.whenStable().then(() => { fixture.detectChanges(); - let actionMenu = fixture.debugElement.nativeElement.querySelectorAll('button.mat-menu-item').length; - expect(fixture.debugElement.nativeElement.querySelector('[data-automation-id="View"]')).not.toBeNull(); - expect(fixture.debugElement.nativeElement.querySelector('[data-automation-id="Remove"]')).toBeNull(); + let actionMenu = window.document.querySelectorAll('button.mat-menu-item').length; + expect(window.document.querySelector('[data-automation-id="ADF_TASK_LIST.MENU_ACTIONS.VIEW_CONTENT"]')).not.toBeNull(); + expect(window.document.querySelector('[data-automation-id="ADF_TASK_LIST.MENU_ACTIONS.DOWNLOAD_CONTENT"]')).not.toBeNull(); + expect(window.document.querySelector('[data-automation-id="ADF_TASK_LIST.MENU_ACTIONS.REMOVE_CONTENT"]')).toBeNull(); expect(actionMenu).toBe(2); }); - }); + })); it('should show the empty list component when the attachments list is empty', async(() => { getTaskRelatedContentSpy.and.returnValue(Observable.of({ @@ -330,12 +339,12 @@ describe('Custom CustomEmptyTemplateComponent', () => { fixture.detectChanges(); }); - it('should render the custom template', () => { + it('should render the custom template', async(() => { fixture.whenStable().then(() => { fixture.detectChanges(); let title: any = fixture.debugElement.queryAll(By.css('[adf-empty-list-header]')); expect(title.length).toBe(1); expect(title[0].nativeElement.innerText).toBe('Custom header'); }); - }); + })); }); diff --git a/lib/process-services/comments/comments.component.spec.ts b/lib/process-services/comments/comments.component.spec.ts index 05036c7f4e..43a3fceeb6 100644 --- a/lib/process-services/comments/comments.component.spec.ts +++ b/lib/process-services/comments/comments.component.spec.ts @@ -104,7 +104,7 @@ describe('CommentsComponent', () => { }); })); - it('should display comments count when the task has comments', () => { + it('should display comments count when the task has comments', async(() => { let change = new SimpleChange(null, '123', true); component.ngOnChanges({ 'taskId': change }); fixture.whenStable().then(() => { @@ -112,7 +112,7 @@ describe('CommentsComponent', () => { let element = fixture.nativeElement.querySelector('#comment-header'); expect(element.innerText).toBe('ADF_TASK_LIST.DETAILS.COMMENTS.HEADER'); }); - }); + })); it('should not display comments when the task has no comments', async(() => { component.taskId = '123'; diff --git a/lib/process-services/comments/process-comments.component.spec.ts b/lib/process-services/comments/process-comments.component.spec.ts index 0df1d4857e..6a43d17501 100644 --- a/lib/process-services/comments/process-comments.component.spec.ts +++ b/lib/process-services/comments/process-comments.component.spec.ts @@ -100,7 +100,7 @@ describe('ActivitiProcessInstanceComments', () => { }); })); - it('should display comments count when the process has comments', () => { + it('should display comments count when the process has comments', async(() => { let change = new SimpleChange(null, '123', true); component.ngOnChanges({ 'processInstanceId': change }); @@ -109,7 +109,7 @@ describe('ActivitiProcessInstanceComments', () => { let element = fixture.nativeElement.querySelector('#comment-header'); expect(element.innerText).toBe('ADF_PROCESS_LIST.DETAILS.COMMENTS.HEADER'); }); - }); + })); it('should not display comments when the process has no comments', async(() => { let change = new SimpleChange(null, '123', true); diff --git a/lib/process-services/mock/process/process-instances-list.mock.ts b/lib/process-services/mock/process/process-instances-list.mock.ts index d5e35d1aba..7d3c42df73 100644 --- a/lib/process-services/mock/process/process-instances-list.mock.ts +++ b/lib/process-services/mock/process/process-instances-list.mock.ts @@ -15,60 +15,75 @@ * limitations under the License. */ -import { ProcessInstance } from '../../process-list/models/process-instance.model'; +export let fakeProcessInstance = { + size: 2, total: 2, start: 0, + data: [ + { + id: '1', name: 'Process 773443333', businessKey: null, + processDefinitionId: 'fakeprocess:5:7507', + tenantId: 'tenant_1', + started: '2015-11-09T12:36:14.184+0000', + ended: null, + startedBy: { id: 3, firstName: 'tenant2', lastName: 'tenantLastname', email: 'tenant2@tenant'}, + processDefinitionName: 'Fake Process Name', + processDefinitionDescription: null, + processDefinitionKey: 'fakeprocess', + processDefinitionCategory: 'http://www.activiti.org/processdef', + processDefinitionVersion: 1, + processDefinitionDeploymentId: '2540', + graphicalNotationDefined: true, + startFormDefined: false, + suspended: false, + variables: [] + }, + { + id: '2', name: 'Process 382927392', businessKey: null, + processDefinitionId: 'fakeprocess:5:7507', + tenantId: 'tenant_1', + started: '2018-01-10T17:02:22.597+0000', + ended: null, + startedBy: { id: 3, firstName: 'tenant2', lastName: 'tenantLastname', email: 'tenant2@tenant' }, + processDefinitionName: 'Fake Process Name', + processDefinitionDescription: null, + processDefinitionKey: 'fakeprocess', + processDefinitionCategory: 'http://www.activiti.org/processdef', + processDefinitionVersion: 1, + processDefinitionDeploymentId: '2540', + graphicalNotationDefined: true, + startFormDefined: false, + suspended: false, + variables: [] + } + ] +}; -export let fakeProcessInstance = [ - new ProcessInstance({ - id: 1, - name: 'Process 773443333', - processDefinitionId: 'fakeprocess:5:7507', - processDefinitionKey: 'fakeprocess', - processDefinitionName: 'Fake Process Name', - description: null, category: null, - started: '2015-11-09T12:36:14.184+0000', - startedBy: { - id: 3, firstName: 'tenant2', lastName: 'tenantLastname', email: 'tenant2@tenant' +export let fakeProcessInstancesWithNoName = { + size: 2, total: 2, start: 0, + data: [ + { + id: '1', + name: null, + processDefinitionId: 'fakeprocess:5:7507', + processDefinitionKey: 'fakeprocess', + processDefinitionName: 'Fake Process Name', + description: null, category: null, + started: '2017-11-09T12:36:14.184+0000', + startedBy: { + id: 3, firstName: 'tenant2', lastName: 'tenantLastname', email: 'tenant2@tenant' + } + }, + { + id: 2, + name: '', + processDefinitionId: 'fakeprocess:5:7507', + processDefinitionKey: 'fakeprocess', + processDefinitionName: 'Fake Process Name', + description: null, + category: null, + started: '2017-11-09T12:37:25.184+0000', + startedBy: { + id: 3, firstName: 'tenant2', lastName: 'tenantLastname', email: 'tenant2@tenant' + } } - }), - new ProcessInstance({ - id: 2, - name: 'Process 382927392', - processDefinitionId: 'fakeprocess:5:7507', - processDefinitionKey: 'fakeprocess', - processDefinitionName: 'Fake Process Name', - description: null, - category: null, - started: '2017-11-09T12:37:25.184+0000', - startedBy: { - id: 3, firstName: 'tenant2', lastName: 'tenantLastname', email: 'tenant2@tenant' - } - }) -]; - -export let fakeProcessInstancesWithNoName = [ - new ProcessInstance({ - id: 1, - name: null, - processDefinitionId: 'fakeprocess:5:7507', - processDefinitionKey: 'fakeprocess', - processDefinitionName: 'Fake Process Name', - description: null, category: null, - started: '2017-11-09T12:36:14.184+0000', - startedBy: { - id: 3, firstName: 'tenant2', lastName: 'tenantLastname', email: 'tenant2@tenant' - } - }), - new ProcessInstance({ - id: 2, - name: '', - processDefinitionId: 'fakeprocess:5:7507', - processDefinitionKey: 'fakeprocess', - processDefinitionName: 'Fake Process Name', - description: null, - category: null, - started: '2017-11-09T12:37:25.184+0000', - startedBy: { - id: 3, firstName: 'tenant2', lastName: 'tenantLastname', email: 'tenant2@tenant' - } - }) -]; + ] +}; diff --git a/lib/process-services/process-list/components/process-list.component.ts b/lib/process-services/process-list/components/process-list.component.ts index 43dc397dd6..bc96d8e55a 100644 --- a/lib/process-services/process-list/components/process-list.component.ts +++ b/lib/process-services/process-list/components/process-list.component.ts @@ -16,20 +16,22 @@ */ import { DataColumn, DataRowEvent, DataSorting, DataTableAdapter, ObjectDataColumn, ObjectDataRow, ObjectDataTableAdapter } from '@alfresco/adf-core'; -import { AppConfigService, DataColumnListComponent } from '@alfresco/adf-core'; +import { AppConfigService, DataColumnListComponent, PaginatedComponent, PaginationComponent, PaginationQueryParams, UserPreferencesService } from '@alfresco/adf-core'; import { DatePipe } from '@angular/common'; import { AfterContentInit, Component, ContentChild, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; import { ProcessFilterParamRepresentationModel } from '../models/filter-process.model'; -import { ProcessInstance } from '../models/process-instance.model'; import { processPresetsDefaultModel } from '../models/process-preset.model'; import { ProcessService } from '../services/process.service'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; +import { Pagination } from 'alfresco-js-api'; +import { ProcessListModel } from '../models/process-list.model'; @Component({ selector: 'adf-process-instance-list', styleUrls: ['./process-list.component.css'], templateUrl: './process-list.component.html' }) -export class ProcessInstanceListComponent implements OnChanges, AfterContentInit { +export class ProcessInstanceListComponent implements OnChanges, AfterContentInit, PaginatedComponent { @ContentChild(DataColumnListComponent) columnList: DataColumnListComponent; @@ -56,6 +58,12 @@ export class ProcessInstanceListComponent implements OnChanges, AfterContentInit name: string; /** The presetColumn of the custom schema to fetch. */ + @Input() + page: number = 0; + + @Input() + size: number = PaginationComponent.DEFAULT_PAGINATION.maxItems; + @Input() presetColumn: string; @@ -71,7 +79,7 @@ export class ProcessInstanceListComponent implements OnChanges, AfterContentInit /** Emitted when the list of process instances has been loaded successfully from the server. */ @Output() - success: EventEmitter = new EventEmitter(); + success: EventEmitter = new EventEmitter(); /** Emitted when an error occurs while loading the list of process instances from the server. */ @Output() @@ -81,8 +89,18 @@ export class ProcessInstanceListComponent implements OnChanges, AfterContentInit isLoading: boolean = true; layoutPresets = {}; + pagination: BehaviorSubject; + constructor(private processService: ProcessService, + private userPreferences: UserPreferencesService, private appConfig: AppConfigService) { + this.size = this.userPreferences.paginationSize; + + this.pagination = new BehaviorSubject( { + maxItems: this.size, + skipCount: 0, + totalItems: 0 + }); } ngAfterContentInit() { @@ -130,6 +148,8 @@ export class ProcessInstanceListComponent implements OnChanges, AfterContentInit let state = changes['state']; let sort = changes['sort']; let name = changes['name']; + let page = changes['page']; + let size = changes['size']; if (appId && appId.currentValue) { changed = true; @@ -141,6 +161,10 @@ export class ProcessInstanceListComponent implements OnChanges, AfterContentInit changed = true; } else if (name && name.currentValue) { changed = true; + } else if (page && page.currentValue !== page.previousValue) { + changed = true; + } else if (size && size.currentValue !== size.previousValue) { + changed = true; } return changed; } @@ -155,11 +179,17 @@ export class ProcessInstanceListComponent implements OnChanges, AfterContentInit this.processService.getProcessInstances(requestNode, this.processDefinitionKey) .subscribe( (response) => { - let instancesRow = this.createDataRow(response); + let instancesRow = this.createDataRow(response.data); this.renderInstances(instancesRow); this.selectFirst(); this.success.emit(response); this.isLoading = false; + this.pagination.next({ + count: response.data.length, + maxItems: this.size, + skipCount: this.page * this.size, + totalItems: response.total + }); }, error => { this.error.emit(error); @@ -297,7 +327,10 @@ export class ProcessInstanceListComponent implements OnChanges, AfterContentInit let requestNode = { appDefinitionId: this.appId, state: this.state, - sort: this.sort + sort: this.sort, + page: this.page, + size: this.size, + start: 0 }; return new ProcessFilterParamRepresentationModel(requestNode); } @@ -323,4 +356,12 @@ export class ProcessInstanceListComponent implements OnChanges, AfterContentInit private getLayoutPreset(name: string = 'default'): DataColumn[] { return (this.layoutPresets[name] || this.layoutPresets['default']).map(col => new ObjectDataColumn(col)); } + + updatePagination(params: PaginationQueryParams) { + + } + + get supportedPageSizes(): number[] { + return this.userPreferences.getDifferentPageSizes(); + } } diff --git a/lib/process-services/process-list/models/process-list.model.ts b/lib/process-services/process-list/models/process-list.model.ts new file mode 100644 index 0000000000..1ebf081621 --- /dev/null +++ b/lib/process-services/process-list/models/process-list.model.ts @@ -0,0 +1,27 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * 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 { ProcessInstance } from './process-instance.model'; + +export interface ProcessListModel { + size: number; + total: number; + start: number; + length: number; + data: ProcessInstance []; + +} diff --git a/lib/process-services/process-list/services/process.service.spec.ts b/lib/process-services/process-list/services/process.service.spec.ts index 4f36e54d25..0fdd5a4aca 100644 --- a/lib/process-services/process-list/services/process.service.spec.ts +++ b/lib/process-services/process-list/services/process.service.spec.ts @@ -64,13 +64,13 @@ describe('ProcessService', () => { it('should return the correct number of instances', async(() => { service.getProcessInstances(filter).subscribe((instances) => { - expect(instances.length).toBe(1); + expect(instances.data.length).toBe(1); }); })); it('should return the correct instance data', async(() => { service.getProcessInstances(filter).subscribe((instances) => { - let instance = instances[0]; + let instance = instances.data[0]; expect(instance.id).toBe(exampleProcess.id); expect(instance.name).toBe(exampleProcess.name); expect(instance.started).toBe(exampleProcess.started); @@ -81,8 +81,8 @@ describe('ProcessService', () => { getProcessInstances = getProcessInstances.and.returnValue(Promise.resolve(fakeProcessInstances)); service.getProcessInstances(filter, 'fakeProcessDefinitionKey1').subscribe((instances) => { - expect(instances.length).toBe(1); - let instance = instances[0]; + expect(instances.data.length).toBe(1); + let instance = instances.data[0]; expect(instance.id).toBe('340124'); expect(instance.name).toBe('James Franklin EMEA Onboarding'); expect(instance.started).toEqual(new Date('2017-10-09T12:19:44.560+0000')); diff --git a/lib/process-services/process-list/services/process.service.ts b/lib/process-services/process-list/services/process.service.ts index b087c37587..64a441e024 100644 --- a/lib/process-services/process-list/services/process.service.ts +++ b/lib/process-services/process-list/services/process.service.ts @@ -23,6 +23,7 @@ import { ProcessFilterParamRepresentationModel } from '../models/filter-process. import { ProcessDefinitionRepresentation } from '../models/process-definition.model'; import { ProcessInstanceVariable } from '../models/process-instance-variable.model'; import { ProcessInstance } from '../models/process-instance.model'; +import { ProcessListModel } from '../models/process-list.model'; import 'rxjs/add/observable/throw'; declare let moment: any; @@ -33,13 +34,15 @@ export class ProcessService { constructor(private alfrescoApiService: AlfrescoApiService) { } - getProcessInstances(requestNode: ProcessFilterParamRepresentationModel, processDefinitionKey?: string): Observable { + getProcessInstances(requestNode: ProcessFilterParamRepresentationModel, processDefinitionKey?: string): Observable { return Observable.fromPromise(this.alfrescoApiService.getInstance().activiti.processApi.getProcessInstances(requestNode)) .map((res: any) => { if (processDefinitionKey) { - return res.data.filter(process => process.processDefinitionKey === processDefinitionKey); + const filtered = res.data.filter(process => process.processDefinitionKey === processDefinitionKey); + res.data = filtered; + return res; } else { - return res.data; + return res; } }).catch(err => this.handleProcessError(err)); } diff --git a/lib/process-services/task-list/components/task-list.component.ts b/lib/process-services/task-list/components/task-list.component.ts index 80a4b297fa..b93263ca46 100644 --- a/lib/process-services/task-list/components/task-list.component.ts +++ b/lib/process-services/task-list/components/task-list.component.ts @@ -16,7 +16,7 @@ */ import { DataColumn, DataRowEvent, DataTableAdapter, ObjectDataColumn, ObjectDataRow, ObjectDataTableAdapter } from '@alfresco/adf-core'; -import { AppConfigService, DataColumnListComponent } from '@alfresco/adf-core'; +import { AppConfigService, DataColumnListComponent, PaginationComponent } from '@alfresco/adf-core'; import { AfterContentInit, Component, ContentChild, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; import { Observable } from 'rxjs/Observable'; import { TaskQueryRequestRepresentationModel } from '../models/filter.model'; @@ -24,7 +24,6 @@ import { TaskListModel } from '../models/task-list.model'; import { taskPresetsDefaultModel } from '../models/task-preset.model'; import { TaskListService } from './../services/tasklist.service'; -const DEFAULT_SIZE = 5; @Component({ selector: 'adf-tasklist', templateUrl: './task-list.component.html', @@ -120,7 +119,7 @@ export class TaskListComponent implements OnChanges, OnInit, AfterContentInit { /* The number of tasks to fetch. */ @Input() - size: number = DEFAULT_SIZE; + size: number = PaginationComponent.DEFAULT_PAGINATION.maxItems; isLoading: boolean = true;