[ADF-3538] [ADF-3547] Migration - Task Filters - Task List - New Process cloud page on demoshell (#3914)

* [ADF-3538] start creating new folder for cloud components

* [ADF-3538] added new package to the script and the builds

* [ADF-3538] added some more changes to scripts

* [ADF-3538] - starting the new package

* change index

* fix package

* Fix module structure with Cli

* add basic structure

* Create a library with angular cli

* Add a cloud component as example

* Skip the scss style

* add the import scss

* remove useless codes

* Add i18n example

* remove useless code

* Simplify the hello component
Fix the wrong path

* add the app-list-cloud-component
add the app-details-cloud-component

* Expose and use the new component

* Consume the new package and component from the demoshell

* Fix process service cloud path

* [ADF-3538] Alfresco Process Service Cloud - new package with CLI (#3872)

* [ADF-3538] start creating new folder for cloud components

* [ADF-3538] added new package to the script and the builds

* [ADF-3538] added some more changes to scripts

* [ADF-3538] - starting the new package

* change index

* fix package

* Fix module structure with Cli

* add basic structure

* Create a library with angular cli

* Add a cloud component as example

* Skip the scss style

* add the import scss

* remove useless codes

* Add i18n example

* remove useless code

* Simplify the hello component
Fix the wrong path

* Fix process service cloud path

* Download process-service-cloud from the CS

* [ADF-3538] generated task-list cloud by cli

* [ADF-3550] Added Task Filter Cloud component

* [ADF-3550] Task Filter Cloud component relocated

* [ADF-3538] rebased task list cloud 2.0

* [ADF-3538] fixed ng-package.json

* [ADF-3538] reverted worng changes

* [ADF-3538] removed wrong rebased files

* [ADF-3538] forcing update of app-list file

* [ADF-3538] wrong file after rebase removed

* [ADF-3538] wrong file after rebase fixe

* Merge the applist component with task list

* [ADF-3550] Added Task Filter Cloud component

* emit the event

* Add the route process cloud and fix the page issues

* fixed wrong pagination initialisation

* improved initialisation of page size

* removed unused import

* fixed tsconfig with double definition

* Use standard name for scss

* fix sorting issue and remove useless models

* Fix tslint

* Use 1 single testing module file

* [ADF-3538] [ADF-3547] Fix selected task filter and unit tests

* Fix unit tests and remove useless imports

* Uncomment unit tests

* Remove useless component

* Use main module instead of submodules

* Remove useless jsdoc and improve doc

* Remove useless jsdoc

* Remove useless interface

* remove AfterViewInit import

* remove js doc params
This commit is contained in:
Maurizio Vitale
2018-10-26 14:39:06 +01:00
committed by Eugenio Romano
parent ee7af9d797
commit 38f4916e06
57 changed files with 2862 additions and 312 deletions

View File

@@ -61,6 +61,7 @@
"NOTIFICATIONS": "Notifications", "NOTIFICATIONS": "Notifications",
"TASK_LIST": "Task List", "TASK_LIST": "Task List",
"PROCESS_LIST": "Process List", "PROCESS_LIST": "Process List",
"PROCESS_CLOUD": "Process Cloud",
"CARD_VIEW": "CardView", "CARD_VIEW": "CardView",
"PROCESS_SERVICES": "Process Services", "PROCESS_SERVICES": "Process Services",
"LOGIN": "Login", "LOGIN": "Login",

View File

@@ -63,8 +63,9 @@ import { ContentModule } from '@alfresco/adf-content-services';
import { InsightsModule } from '@alfresco/adf-insights'; import { InsightsModule } from '@alfresco/adf-insights';
import { ProcessModule } from '@alfresco/adf-process-services'; import { ProcessModule } from '@alfresco/adf-process-services';
import { AuthBearerInterceptor } from './services'; import { AuthBearerInterceptor } from './services';
import { AppListCloudModule } from '@alfresco/adf-process-services-cloud'; import { ProcessServicesCloudModule } from '@alfresco/adf-process-services-cloud';
import { CloudComponent } from './components/cloud/cloud.component'; import { CloudComponent } from './components/cloud/cloud.component';
import { TaskListCloudDemoComponent } from './components/task-list-cloud-demo/task-list-cloud-demo.component';
@NgModule({ @NgModule({
imports: [ imports: [
@@ -83,7 +84,7 @@ import { CloudComponent } from './components/cloud/cloud.component';
ThemePickerModule, ThemePickerModule,
ChartsModule, ChartsModule,
MonacoEditorModule.forRoot(), MonacoEditorModule.forRoot(),
AppListCloudModule ProcessServicesCloudModule
], ],
declarations: [ declarations: [
AppComponent, AppComponent,
@@ -113,7 +114,8 @@ import { CloudComponent } from './components/cloud/cloud.component';
FormLoadingComponent, FormLoadingComponent,
DemoPermissionComponent, DemoPermissionComponent,
FormLoadingComponent, FormLoadingComponent,
ReportIssueComponent ReportIssueComponent,
TaskListCloudDemoComponent
], ],
providers: [ providers: [
{ {

View File

@@ -41,6 +41,7 @@ import { DemoPermissionComponent } from './components/permissions/demo-permissio
import { ReportIssueComponent } from './components/report-issue/report-issue.component'; import { ReportIssueComponent } from './components/report-issue/report-issue.component';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { CloudComponent } from './components/cloud/cloud.component'; import { CloudComponent } from './components/cloud/cloud.component';
import { TaskListCloudDemoComponent } from './components/task-list-cloud-demo/task-list-cloud-demo.component';
export const appRoutes: Routes = [ export const appRoutes: Routes = [
{ path: 'login', component: LoginComponent }, { path: 'login', component: LoginComponent },
@@ -135,8 +136,17 @@ export const appRoutes: Routes = [
}, },
{ {
path: 'cloud', path: 'cloud',
children: [
{
path: '',
component: CloudComponent component: CloudComponent
}, },
{
path: ':applicationName/tasks',
component: TaskListCloudDemoComponent
}
]
},
{ {
path: 'node-selector', path: 'node-selector',
loadChildren: 'app/components/content-node-selector/content-node-selector.module#AppContentNodeSelectorModule' loadChildren: 'app/components/content-node-selector/content-node-selector.module#AppContentNodeSelectorModule'

View File

@@ -40,6 +40,7 @@ export class AppLayoutComponent implements OnInit {
{ href: '/node-selector', icon: 'attachment', title: 'APP_LAYOUT.NODE-SELECTOR' }, { href: '/node-selector', icon: 'attachment', title: 'APP_LAYOUT.NODE-SELECTOR' },
{ href: '/task-list', icon: 'assignment', title: 'APP_LAYOUT.TASK_LIST' }, { href: '/task-list', icon: 'assignment', title: 'APP_LAYOUT.TASK_LIST' },
{ href: '/process-list', icon: 'assignment', title: 'APP_LAYOUT.PROCESS_LIST' }, { href: '/process-list', icon: 'assignment', title: 'APP_LAYOUT.PROCESS_LIST' },
{ href: '/cloud', icon: 'cloud', title: 'APP_LAYOUT.PROCESS_CLOUD' },
{ href: '/activiti', icon: 'device_hub', title: 'APP_LAYOUT.PROCESS_SERVICES' }, { href: '/activiti', icon: 'device_hub', title: 'APP_LAYOUT.PROCESS_SERVICES' },
{ href: '/login', icon: 'vpn_key', title: 'APP_LAYOUT.LOGIN' }, { href: '/login', icon: 'vpn_key', title: 'APP_LAYOUT.LOGIN' },
{ href: '/trashcan', icon: 'delete', title: 'APP_LAYOUT.TRASHCAN' }, { href: '/trashcan', icon: 'delete', title: 'APP_LAYOUT.TRASHCAN' },

View File

@@ -1,2 +1,2 @@
<adf-cloud-app-list></adf-cloud-app-list> <adf-cloud-app-list (appClick)="onAppClick($event)"></adf-cloud-app-list>

View File

@@ -16,7 +16,7 @@
*/ */
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
@Component({ @Component({
selector: 'app-cloud', selector: 'app-cloud',
templateUrl: './cloud.component.html', templateUrl: './cloud.component.html',
@@ -24,9 +24,13 @@ import { Component, OnInit } from '@angular/core';
}) })
export class CloudComponent implements OnInit { export class CloudComponent implements OnInit {
constructor() { constructor(private router: Router) {
} }
ngOnInit() { ngOnInit() {
} }
onAppClick(app) {
this.router.navigate([`/cloud/${app.name}/tasks/`]);
}
} }

View File

@@ -0,0 +1,86 @@
<h2>TASK LIST CLOUD DEMO</h2>
<adf-cloud-task-filters
[appName]="'my-app'"
[showIcons]="true"
(filterClick)="onFilterSelected($event)">
</adf-cloud-task-filters>
<mat-accordion>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
{{filterName}}
</mat-panel-title>
<mat-panel-description>
Customise your filter
</mat-panel-description>
</mat-expansion-panel-header>
<div class="task-cloud-demo-select">
<mat-form-field style="margin: 8px;">
<mat-select placeholder="Status" [(ngModel)]="status">
<mat-option value="">
ALL
</mat-option>
<mat-option value="CREATED">
CREATED
</mat-option>
<mat-option value="CANCELLED">
CANCELLED
</mat-option>
<mat-option value="ASSIGNED">
ASSIGNED
</mat-option>
<mat-option value="SUSPENDED">
SUSPENDED
</mat-option>
<mat-option value="COMPLETED">
COMPLETED
</mat-option>
<mat-option value="DELETED">
DELETED
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<mat-select [formControl]="sortFormControl">
<mat-option [value]="''">Select a column</mat-option>
<mat-option *ngFor="let column of columns" [value]="column.key">
{{column.label}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<mat-select [formControl]="sortDirectionFormControl">
<mat-option [value]="''">Select a direction</mat-option>
<mat-option value="ASC">
ASC
</mat-option>
<mat-option value="DESC">
DESC
</mat-option>
</mat-select>
</mat-form-field>
</div>
</mat-expansion-panel>
</mat-accordion>
<div>
<adf-cloud-task-list *ngIf="isFilterLoaded" #taskCloud
[applicationName]="applicationName"
[status]="status"
[landingTaskId]="selectTask"
[sorting]="sortArray"
(rowClick)="onRowClick($event)">
<data-columns>
<data-column key="entry.id" title="Id"></data-column>
<data-column key="entry.name" title="Name"></data-column>
<data-column key="entry.status" title="Status"></data-column>
<data-column key="entry.processDefinitionId" title="Process Definition Id"></data-column>
<data-column key="entry.priority" title="Priority"></data-column>
<data-column key="entry.createdDate" title="Created Date"></data-column>
</data-columns>
</adf-cloud-task-list>
<adf-pagination [target]="taskCloud"
(changePageSize)="onChangePageSize($event)">
</adf-pagination>
</div>

View File

@@ -0,0 +1,15 @@
.task-cloud-demo-select {
padding: 10px;
}
.task-cloud-demo-select .mat-expansion-panel-body {
display: flex;
}
.app-task-list-cloud-demo-selection{
display: flex;
}
.task-row-clicked {
align-self: center;
}

View File

@@ -0,0 +1,126 @@
/*!
* @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 { Component, ViewChild, OnInit } from '@angular/core';
import { TaskListCloudComponent, TaskListCloudSortingModel } from '@alfresco/adf-process-services-cloud';
import { UserPreferencesService } from '@alfresco/adf-core';
import { Observable } from 'rxjs';
import { ActivatedRoute, Router } from '@angular/router';
import { FormControl } from '@angular/forms';
@Component({
selector: 'app-task-list-cloud-demo',
templateUrl: 'task-list-cloud-demo.component.html',
styleUrls: ['task-list-cloud-demo.component.scss']
})
export class TaskListCloudDemoComponent implements OnInit {
@ViewChild('taskCloud')
taskCloud: TaskListCloudComponent;
sortFormControl: FormControl;
sortDirectionFormControl: FormControl;
appDefinitionList: Observable<any>;
applicationName: string = '';
status: string = '';
sort: string = '';
isFilterLoaded = false;
sortDirection: string = 'ASC';
filterName: string;
clickedRow: string = '';
selectTask: string = '';
sortArray: TaskListCloudSortingModel [];
columns = [
{key: 'id', label: 'ID'},
{key: 'name', label: 'NAME'},
{key: 'createdDate', label: 'Created Date'},
{key: 'priority', label: 'PRIORITY'},
{key: 'processDefinitionId', label: 'PROCESS DEFINITION ID'}
];
constructor(
private route: ActivatedRoute,
private router: Router,
private userPreference: UserPreferencesService) {
}
ngOnInit() {
this.isFilterLoaded = false;
this.route.params.subscribe(params => {
this.applicationName = params.applicationName;
});
this.sortFormControl = new FormControl('');
this.sortFormControl.valueChanges.subscribe(
(sortValue) => {
this.sort = sortValue;
this.sortArray = [{
orderBy: this.sort,
direction: this.sortDirection
}];
}
);
this.sortDirectionFormControl = new FormControl('');
this.sortDirectionFormControl.valueChanges.subscribe(
(sortDirectionValue) => {
this.sortDirection = sortDirectionValue;
this.sortArray = [{
orderBy: this.sort,
direction: this.sortDirection
}];
}
);
this.route.queryParams
.subscribe(params => {
if (params.status) {
this.status = params.status;
this.sort = params.sort;
this.sortDirection = params.order;
this.filterName = params.filterName;
this.isFilterLoaded = true;
this.sortDirectionFormControl.setValue(this.sortDirection);
this.sortFormControl.setValue(this.sort);
}
});
}
onFilterSelected(filter) {
const queryParams = {
status: filter.query.state,
filterName: filter.name,
sort: filter.query.sort,
order: filter.query.order
};
this.router.navigate([`/cloud/${this.applicationName}/tasks/`], {queryParams: queryParams});
}
onChangePageSize(event) {
this.userPreference.paginationSize = event.maxItems;
}
onRowClick($event) {
this.clickedRow = $event;
}
}

View File

@@ -0,0 +1,217 @@
---
Added: v2.0.0
Status: Active
Last reviewed: 2018-04-16
---
# Task List component
Renders a list containing all the tasks matched by the parameters specified.
## Contents
- [Basic Usage](#basic-usage)
- [Transclusions](#transclusions)
- [Class members](#class-members)
- [Properties](#properties)
- [Events](#events)
- [Details](#details)
- [Setting the column schema](#setting-the-column-schema)
- [Setting Sorting Order for the list](#setting-sorting-order-for-the-list)
- [Pagination strategy](#pagination-strategy)
- [DataTableAdapter example](#datatableadapter-example)
- [DataColumn Features](#datacolumn-features)
- [See also](#see-also)
## Basic Usage
```html
<adf-cloud-task-list
[applicationName]="'APPLICATION-NAME'" >
</adf-cloud-task-list>
```
### [Transclusions](../user-guide/transclusion.md)
Any content inside an `<adf-empty-custom-content>` sub-component will be shown
when the task list is empty:
```html
<adf-cloud-task-list>
<adf-empty-custom-content>
Your Content
</adf-empty-custom-content>
<adf-cloud-task-list>
```
## Class members
### Properties
| Name | Type | Default value | Description |
| ---- | ---- | ------------- | ----------- |
| applicationName | `string` | | The name of the application. |
| assignee | `string` | | The assigee of the process. Possible values are: "assignee" (the current user is the assignee), candidate (the current user is a task candidate", "group_x" (the task is assigned to a group where the current user is a member, no value(the current user is involved). |
| createdDate | `Date` | | filter the tasks for the date when the task should have been created |
| dueDate | `Date` | | Filter the tasks. Display only tasks with dueDate equal to the one insterted. |
| id | `string` | | Filter the tasks. Display only tasks with id equal to the one insterted. |
| name | `string` | | Filter the tasks. Display only tasks with name equal to the one insterted. |
| parentTaskId | `string` | | Filter the tasks. Display only tasks with parentTaskId equal to the one insterted. |
| processDefinitionId | `string` | | Filter the tasks. Display only tasks with processDefinitionId equal to the one insterted. |
| processInstanceId | `string` | | Filter the tasks. Display only tasks with processInstanceId equal to the one insterted. |
| status | `string` | | Filter the tasks. Display only tasks with status equal to the one insterted. |
| processDefinitionId | `string` | | Filter the tasks. Display only tasks with processDefinitionId equal to the one insterted. |
| landingTaskId | `string` | | Define which task id should be selected after reloading. If the task id doesn't exist or nothing is passed then the first task will be selected. |
| selectFirstRow | `boolean` | true | Toggles default selection of the first row |
| 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 |
| sorting | `[TaskListCloudSortingModel]` | | This array of `TaskListCloudSortingModel` specify how the sorting on our table should be provided. This parameters are for BE sorting. |
### Events
| Name | Type | Description |
| ---- | ---- | ----------- |
| error | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<any>` | Emitted when an error occurs. |
| rowClick | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<string>` | Emitted when a task in the list is clicked |
| rowsSelected | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<any[]>` | Emitted when rows are selected/unselected |
| success | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<any>` | Emitted when the task list is loaded |
## Details
This component displays lists of tasks related to the application name insterted. Extra filtering can be provided by applying extra input parameters
### Setting the column schema
You can use an HTML-based schema declaration to set a column schema for the tasklist as shown below :
```html
<adf-cloud-task-list ...>
<data-columns>
<data-column key="name" title="NAME" class="full-width name-column"></data-column>
<data-column key="created" title="Created" class="hidden"></data-column>
</data-columns>
</adf-cloud-task-list>
```
You can also set a static custom schema declaration in `app.config.json` as shown below:
```json
"adf-cloud-task-list": {
"presets": {
"customSchema": [
{
"key": "name",
"type": "text",
"title": "name",
"sortable": true
}],
"default": [
{
"key": "name",
"type": "text",
"title": "name",
"sortable": true
}],
}
}
```
```html
<adf-cloud-task-list
[appId]="'1'"
[presetColumn]="'customSchema'">
</adf-cloud-task-list>
```
You can use an HTML-based schema and an `app.config.json` custom schema declaration at the same time:
```json
"adf-cloud-task-list": {
"presets": {
"customSchema": [
{
"key": "id",
"type": "text",
"title": "Id",
"sortable": true
}],
"default": [
{
"key": "name",
"type": "text",
"title": "name",
"sortable": true
}],
}
}
```
<!-- {% raw %} -->
```html
<adf-cloud-task-list
[applicationName]="'ApplicationName'">
<data-columns>
<data-column key="assignee" title="Assignee" class="full-width name-column">
<ng-template let-entry="$implicit">
<div>{{getFullName(entry.row.obj.assignee)}}</div>
</ng-template>
</data-column>
</data-columns>
</adf-cloud-task-list>
```
### Setting Sorting Order for the list
you can pass sorting order as shown in the example below:
```ts
let sorting = { orderBy: 'created', direction: 'desc' };
```
```html
<adf-cloud-task-list
[appId]="'1'"
[sorting]="[sorting]">
</adf-cloud-task-list>
```
<!-- {% endraw %} -->
### Pagination strategy
The Tasklist also supports pagination as shown in the example below:
```html
<adf-cloud-task-list #taskCloud
[applicationName]="'APPLICATION-NAME'">
</adf-cloud-task-list>
<adf-pagination [target]="taskCloud"
(changePageSize)="onChangePageSize($event)">
</adf-pagination>
```
### DataTableAdapter example
See the [`DataTableAdapter`](../../lib/core/datatable/data/datatable-adapter.ts) page for full details of the interface and its standard
implementation, [`ObjectDataTableAdapter`](../../lib/core/datatable/data/object-datatable-adapter.ts). Below is an example of how you can set up the adapter for a
typical tasklist.
```json
[
{"type": "text", "key": "id", "title": "Id"},
{"type": "text", "key": "name", "title": "Name", "cssClass": "full-width name-column", "sortable": true},
{"type": "text", "key": "formKey", "title": "Form Key", "sortable": true},
{"type": "text", "key": "created", "title": "Created", "sortable": true}
]
```
### DataColumn Features
You can customize the styling of a column and also add features like tooltips and automatic translation of column titles. See the [`DataColumn`](../../lib/core/datatable/data/data-column.model.ts) page for more information about these features.
## See also
- [Data column component](../core/data-column.component.md)
- [`DataTableAdapter`](../../lib/core/datatable/data/datatable-adapter.ts)
- [Pagination component](../core/pagination.component.md)

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="20px" height="16px" viewBox="0 0 20 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
<title>System Icon Links</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Style" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="icons" transform="translate(-289.000000, -4488.000000)">
<g id="File" transform="translate(140.000000, 4130.000000)">
<g id="Icon-Links-Folder" transform="translate(120.000000, 328.000000)">
<g id="System-Icon-Links" transform="translate(28.000000, 30.000000)">
<path d="M1.70933755,0.543276016 L1,1.924926 L1,14.8121399 L1.69466158,15.5150573 L1.69466158,15.5150573 C1.88252616,15.7051546 2.13866895,15.8121399 2.40593302,15.8121399 L19.6045166,15.8121399 L20.3116234,15.1050331 L20.3116234,15.1050331 C20.4991598,14.9174967 20.6045166,14.6631428 20.6045166,14.3979263 L20.6045166,3.00924449 L19.9015261,2.24697884 L19.9015261,2.24697884 C19.7122055,2.04169503 19.4456711,1.924926 19.1664154,1.924926 L10.1733882,1.924926 L10.1733882,1.924926 C9.85108856,1.924926 9.548546,1.76958526 9.36072924,1.50766556 L8.57882415,0.417260445 L8.57882415,0.417260445 C8.39100739,0.155340737 8.08846483,-6.31724684e-16 7.76616517,0 L2.59894601,0 L2.59894601,-1.11022302e-16 C2.2240117,-4.21479881e-17 1.88057904,0.209731289 1.70933755,0.543276016 Z" id="Path-2-Copy" fill="#000000" opacity="0.54"></path>
<path d="M10,6 L7.6,6 L7.6,7.14 L10,7.14 C11.026,7.14 11.86,7.974 11.86,9 C11.86,10.026 11.026,10.86 10,10.86 L7.6,10.86 L7.6,12 L10,12 C11.656,12 13,10.656 13,9 C13,7.344 11.656,6 10,6 L10,6 Z M4.6,9.6 L9.4,9.6 L9.4,8.4 L4.6,8.4 L4.6,9.6 Z M2.14,9 C2.14,7.974 2.974,7.14 4,7.14 L6.4,7.14 L6.4,6 L4,6 C2.344,6 1,7.344 1,9 C1,10.656 2.344,12 4,12 L6.4,12 L6.4,10.86 L4,10.86 C2.974,10.86 2.14,10.026 2.14,9 L2.14,9 Z" id="Fill-2" fill="#FFFFFF" transform="translate(7.000000, 9.000000) rotate(-45.000000) translate(-7.000000, -9.000000) "></path>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -18,5 +18,6 @@
export * from './context-menu-holder.component'; export * from './context-menu-holder.component';
export * from './context-menu.directive'; export * from './context-menu.directive';
export * from './context-menu.service'; export * from './context-menu.service';
export * from './context-menu-overlay.service';
export * from './context-menu.module'; export * from './context-menu.module';

View File

@@ -1,30 +0,0 @@
npm-debug.log
.idea
.npmrc
/.editorconfig
/.travis.yml
/*.json
/karma-test-shim.js
/karma.conf.js
/gulpfile.ts
/.npmignore
/.happypack
**/*.html
**/*.js
**/*.ts
!**/*.d.ts
!**/adf-process-services-cloud.js
**/*.scss
**/*.css
!**/_theming.scss
coverage/
demo/
dist/
node_modules
typings/
fonts/
i18n/
assets/

View File

@@ -15,4 +15,4 @@
* limitations under the License. * limitations under the License.
*/ */
export * from './src/public_api'; export * from './src/public-api';

View File

@@ -1,44 +0,0 @@
/*!
* @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 { NgModule } from '@angular/core';
import {
MAT_LABEL_GLOBAL_OPTIONS, MatAutocompleteModule, MatButtonModule, MatCardModule, MatCheckboxModule,
MatChipsModule, MatDatepickerModule, MatDialogModule, MatGridListModule, MatIconModule,
MatInputModule, MatListModule, MatNativeDateModule, MatOptionModule, MatProgressSpinnerModule, MatRadioModule,
MatRippleModule, MatSelectModule, MatSlideToggleModule, MatTableModule, MatTabsModule,
MatTooltipModule, MatMenuModule
} from '@angular/material';
export function modules() {
return [
MatAutocompleteModule, MatButtonModule, MatCardModule, MatDialogModule,
MatCheckboxModule, MatDatepickerModule, MatGridListModule, MatIconModule, MatInputModule,
MatListModule, MatOptionModule, MatRadioModule, MatSelectModule, MatSlideToggleModule, MatTableModule,
MatTabsModule, MatProgressSpinnerModule, MatNativeDateModule, MatRippleModule, MatTooltipModule,
MatChipsModule, MatMenuModule
];
}
@NgModule({
providers: [
{provide: MAT_LABEL_GLOBAL_OPTIONS, useValue: { float: 'never' }}
],
imports: modules(),
exports: modules()
})
export class MaterialModule {}

View File

@@ -3,7 +3,7 @@
"dest": "../dist/process-services-cloud", "dest": "../dist/process-services-cloud",
"lib": { "lib": {
"languageLevel": ["dom", "es2017"], "languageLevel": ["dom", "es2017"],
"entryFile": "src/public_api.ts", "entryFile": "src/public-api.ts",
"flatModuleFile": "adf-process-services-cloud", "flatModuleFile": "adf-process-services-cloud",
"umdModuleIds": { "umdModuleIds": {
"alfresco-js-api": "alfresco-js-api" "alfresco-js-api": "alfresco-js-api"

View File

@@ -1,99 +0,0 @@
/*!
* @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 { CommonModule } from '@angular/common';
import { NgModule, ModuleWithProviders } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { CoreModule, TRANSLATION_PROVIDER } from '@alfresco/adf-core';
import { MaterialModule } from './material.module';
import { HelloCloudModule } from './hello/hello.module';
export function providers() {
return [
];
}
@NgModule({
imports: [
CoreModule.forChild(),
CommonModule,
FormsModule,
ReactiveFormsModule,
MaterialModule,
HelloCloudModule
],
providers: [
...providers(),
{
provide: TRANSLATION_PROVIDER,
multi: true,
useValue: {
name: 'adf-process-services-cloud',
source: 'assets/adf-process-services-cloud'
}
}
],
exports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
HelloCloudModule
]
})
export class ProcessCloudModule {
static forRoot(): ModuleWithProviders {
return {
ngModule: ProcessCloudModule,
providers: [
...providers(),
{
provide: TRANSLATION_PROVIDER,
multi: true,
useValue: {
name: 'adf-process-services-cloud',
source: 'assets/adf-process-services-cloud'
}
}
]
};
}
static forChild(): ModuleWithProviders {
return {
ngModule: ProcessCloudModuleLazy
};
}
}
@NgModule({
imports: [
CoreModule.forChild(),
CommonModule,
FormsModule,
ReactiveFormsModule,
MaterialModule,
HelloCloudModule
],
exports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
HelloCloudModule
]
})
export class ProcessCloudModuleLazy {}

View File

@@ -20,7 +20,8 @@ import { setupTestBed } from '@alfresco/adf-core';
import { fakeApplicationInstance } from '../mock/app-model.mock'; import { fakeApplicationInstance } from '../mock/app-model.mock';
import { AppDetailsCloudComponent } from './app-details-cloud.component'; import { AppDetailsCloudComponent } from './app-details-cloud.component';
import { AppListTestingModule } from '../testing/app-list.testing.module'; import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
import { AppListCloudModule } from '../app-list-cloud.module';
describe('AppDetailsCloudComponent', () => { describe('AppDetailsCloudComponent', () => {
@@ -28,7 +29,7 @@ describe('AppDetailsCloudComponent', () => {
let fixture: ComponentFixture<AppDetailsCloudComponent>; let fixture: ComponentFixture<AppDetailsCloudComponent>;
setupTestBed({ setupTestBed({
imports: [AppListTestingModule] imports: [ProcessServiceCloudTestingModule, AppListCloudModule]
}); });
beforeEach(() => { beforeEach(() => {

View File

@@ -23,8 +23,9 @@ import { of } from 'rxjs';
import { fakeApplicationInstance } from '../mock/app-model.mock'; import { fakeApplicationInstance } from '../mock/app-model.mock';
import { AppListCloudComponent } from './app-list-cloud.component'; import { AppListCloudComponent } from './app-list-cloud.component';
import { AppsProcessCloudService } from '../services/apps-process-cloud.service'; import { AppsProcessCloudService } from '../services/apps-process-cloud.service';
import { AppListTestingModule } from '../testing/app-list.testing.module'; import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
import { ApplicationInstanceModel } from '../models/application-instance.model'; import { ApplicationInstanceModel } from '../models/application-instance.model';
import { AppListCloudModule } from '../app-list-cloud.module';
describe('AppListCloudComponent', () => { describe('AppListCloudComponent', () => {
@@ -34,7 +35,7 @@ describe('AppListCloudComponent', () => {
let getAppsSpy: jasmine.Spy; let getAppsSpy: jasmine.Spy;
setupTestBed({ setupTestBed({
imports: [AppListTestingModule], imports: [ProcessServiceCloudTestingModule, AppListCloudModule],
providers: [AppsProcessCloudService] providers: [AppsProcessCloudService]
}); });
@@ -184,7 +185,7 @@ describe('Custom CustomEmptyAppListCloudTemplateComponent', () => {
let fixture: ComponentFixture<CustomEmptyAppListCloudTemplateComponent>; let fixture: ComponentFixture<CustomEmptyAppListCloudTemplateComponent>;
setupTestBed({ setupTestBed({
imports: [AppListTestingModule], imports: [ProcessServiceCloudTestingModule],
declarations: [CustomEmptyAppListCloudTemplateComponent], declarations: [CustomEmptyAppListCloudTemplateComponent],
schemas: [ CUSTOM_ELEMENTS_SCHEMA ] schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
}); });

View File

@@ -17,4 +17,4 @@
export * from './components/app-list-cloud.component'; export * from './components/app-list-cloud.component';
export * from './models/application-instance.model'; export * from './models/application-instance.model';
export * from './apps-list-cloud.module'; export * from './app-list-cloud.module';

View File

@@ -22,14 +22,14 @@ import { HttpErrorResponse } from '@angular/common/http';
import { AppsProcessCloudService } from './apps-process-cloud.service'; import { AppsProcessCloudService } from './apps-process-cloud.service';
import { fakeApplicationInstance } from '../mock/app-model.mock'; import { fakeApplicationInstance } from '../mock/app-model.mock';
import { ApplicationInstanceModel } from '../models/application-instance.model'; import { ApplicationInstanceModel } from '../models/application-instance.model';
import { AppListTestingModule } from '../testing/app-list.testing.module'; import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
describe('AppsProcessCloudService', () => { describe('AppsProcessCloudService', () => {
let service: AppsProcessCloudService; let service: AppsProcessCloudService;
setupTestBed({ setupTestBed({
imports: [AppListTestingModule], imports: [ProcessServiceCloudTestingModule],
providers: [AppsProcessCloudService] providers: [AppsProcessCloudService]
}); });

View File

@@ -1,4 +0,0 @@
<p>
hello cloud world!
</p>

View File

@@ -1,25 +0,0 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { HelloComponent } from './hello.component';
describe('HelloComponent', () => {
let component: HelloComponent;
let fixture: ComponentFixture<HelloComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ HelloComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(HelloComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -1,15 +0,0 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'adf-cloud-hello',
templateUrl: './hello.component.html',
styleUrls: ['./hello.component.css']
})
export class HelloComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}

View File

@@ -1,13 +0,0 @@
import { HelloModule } from './hello.module';
describe('HelloModule', () => {
let helloModule: HelloModule;
beforeEach(() => {
helloModule = new HelloModule();
});
it('should create an instance', () => {
expect(helloModule).toBeTruthy();
});
});

View File

@@ -1,13 +0,0 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HelloComponent } from './hello.component';
import { TranslateModule } from '@ngx-translate/core';
@NgModule({
imports: [
CommonModule,
TranslateModule
],
declarations: [HelloComponent],
exports: [HelloComponent]
})
export class HelloModule { }

View File

@@ -1,10 +1,14 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { TRANSLATION_PROVIDER } from '@alfresco/adf-core'; import { TRANSLATION_PROVIDER } from '@alfresco/adf-core';
import { AppListCloudModule } from './app-list-cloud/app-list-cloud.module'; import { AppListCloudModule } from './app-list-cloud/app-list-cloud.module';
import { TaskListCloudModule } from './task-list-cloud/task-list-cloud.module';
import { TaskCloudModule } from './task-cloud/task-cloud.module';
@NgModule({ @NgModule({
imports: [ imports: [
AppListCloudModule AppListCloudModule,
TaskListCloudModule,
TaskCloudModule
], ],
providers: [ providers: [
{ {
@@ -17,6 +21,6 @@ import { AppListCloudModule } from './app-list-cloud/app-list-cloud.module';
} }
], ],
declarations: [], declarations: [],
exports: [AppListCloudModule] exports: [AppListCloudModule, TaskListCloudModule, TaskCloudModule]
}) })
export class ProcessServicesCloudModule { } export class ProcessServicesCloudModule { }

View File

@@ -1,7 +1,9 @@
@import './../app-list-cloud/components/app-details-cloud.component'; @import './../app-list-cloud/components/app-details-cloud.component';
@import './../app-list-cloud/components/app-list-cloud.component'; @import './../app-list-cloud/components/app-list-cloud.component';
@import './../task-cloud/task-filters-cloud/task-filters-cloud.component.scss';
@mixin adf-process-services-cloud-theme($theme) { @mixin adf-process-services-cloud-theme($theme) {
@include adf-cloud-app-list-theme($theme); @include adf-cloud-app-list-theme($theme);
@include adf-cloud-app-details-theme($theme); @include adf-cloud-app-details-theme($theme);
@include adf-cloud-task-filters-theme($theme);
} }

View File

@@ -0,0 +1,267 @@
/*!
* @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 { FilterRepresentationModel, AppDefinitionRepresentationModel } from '../models/filter-cloud.model';
export let fakeFilters = {
size: 2, total: 2, start: 0,
data: [
new AppDefinitionRepresentationModel(
{
id: 1, name: 'FakeInvolvedTasks', recent: false, icon: 'glyphicon-align-left',
filter: { sort: 'created-desc', name: '', state: 'open', assignment: 'fake-involved' }
}
),
{
id: 2, name: 'FakeMyTasks', recent: false, icon: 'glyphicon-align-left',
filter: { sort: 'created-desc', name: '', state: 'open', assignment: 'fake-assignee' }
}
]
};
export let fakeAppFilter = {
size: 1, total: 1, start: 0,
data: [
{
id: 1, name: 'FakeInvolvedTasks', recent: false, icon: 'glyphicon-align-left',
filter: { sort: 'created-desc', name: '', state: 'open', assignment: 'fake-involved' }
}
]
};
export let fakeApps = {
size: 2, total: 2, start: 0,
data: [
{
id: 1, defaultAppId: null, name: 'Sales-Fakes-App', description: 'desc-fake1', modelId: 22,
theme: 'theme-1-fake', icon: 'glyphicon-asterisk', 'deploymentId': '111', 'tenantId': null
},
{
id: 2, defaultAppId: null, name: 'health-care-Fake', description: 'desc-fake2', modelId: 33,
theme: 'theme-2-fake', icon: 'glyphicon-asterisk', 'deploymentId': '444', 'tenantId': null
}
]
};
export let fakeFilter = {
sort: 'created-desc', text: '', state: 'open', assignment: 'fake-assignee'
};
export let fakeUser1 = { id: 1, email: 'fake-email@dom.com', firstName: 'firstName', lastName: 'lastName' };
export let fakeUser2 = { id: 1001, email: 'some-one@somegroup.com', firstName: 'some', lastName: 'one' };
export let fakeTaskList = {
size: 1, total: 1, start: 0,
data: [
{
id: '1', name: 'FakeNameTask', description: null, category: null,
assignee: fakeUser1,
created: '2016-07-15T11:19:17.440+0000'
}
]
};
export let fakeTaskListDifferentProcessDefinitionKey = {
size: 2, total: 1, start: 0,
data: [
{
id: '1', name: 'FakeNameTask', description: null, category: null,
assignee: fakeUser1,
processDefinitionKey: '1',
created: '2016-07-15T11:19:17.440+0000'
},
{
id: '2', name: 'FakeNameTask2', description: null, category: null,
assignee: fakeUser1,
processDefinitionKey: '2',
created: '2016-07-15T11:19:17.440+0000'
}
]
};
export let secondFakeTaskList = {
size: 1, total: 1, start: 0,
data: [
{
id: '200', name: 'FakeNameTask', description: null, category: null,
assignee: fakeUser1,
created: '2016-07-15T11:19:17.440+0000'
}
]
};
export let mockErrorTaskList = {
error: 'wrong request'
};
export let fakeTaskDetails = { id: '999', name: 'fake-task-name', formKey: '99', assignee: fakeUser1 };
export let fakeTasksComment = {
size: 2, total: 2, start: 0,
data: [
{
id: 1, message: 'fake-message-1', created: '', createdBy: fakeUser1
},
{
id: 2, message: 'fake-message-2', created: '', createdBy: fakeUser1
}
]
};
export let fakeTasksChecklist = {
size: 1, total: 1, start: 0,
data: [
{
id: 1, name: 'FakeCheckTask1', description: null, category: null,
assignee: fakeUser1,
created: '2016-07-15T11:19:17.440+0000'
},
{
id: 2, name: 'FakeCheckTask2', description: null, category: null,
assignee: fakeUser1,
created: '2016-07-15T11:19:17.440+0000'
}
]
};
export let fakeRepresentationFilter1: FilterRepresentationModel = new FilterRepresentationModel({
appId: 1,
name: 'CONTAIN FILTER',
recent: true,
icon: 'glyphicon-align-left',
filter: {
processDefinitionId: null,
processDefinitionKey: null,
name: null,
state: 'open',
sort: 'created-desc',
assignment: 'involved',
dueAfter: null,
dueBefore: null
}
});
export let fakeRepresentationFilter2: FilterRepresentationModel = new FilterRepresentationModel({
appId: 2,
name: 'NO TASK FILTER',
recent: false,
icon: 'glyphicon-inbox',
filter: {
processDefinitionId: null,
processDefinitionKey: null,
name: null,
state: 'open',
sort: 'created-desc',
assignment: 'assignee',
dueAfter: null,
dueBefore: null
}
});
export let fakeAppPromise = new Promise(function (resolve, reject) {
resolve(fakeAppFilter);
});
export let fakeFormList = {
size: 2,
total: 2,
start: 0,
data: [{
id: 1,
name: 'form with all widgets',
description: '',
createdBy: 2,
createdByFullName: 'Admin Admin',
lastUpdatedBy: 2,
lastUpdatedByFullName: 'Admin Admin',
lastUpdated: 1491400951205,
latestVersion: true,
version: 4,
comment: null,
stencilSet: null,
referenceId: null,
modelType: 2,
favorite: null,
permission: 'write',
tenantId: null
}, {
id: 2,
name: 'uppy',
description: '',
createdBy: 2,
createdByFullName: 'Admin Admin',
lastUpdatedBy: 2,
lastUpdatedByFullName: 'Admin Admin',
lastUpdated: 1490951054477,
latestVersion: true,
version: 2,
comment: null,
stencilSet: null,
referenceId: null,
modelType: 2,
favorite: null,
permission: 'write',
tenantId: null
}]
};
export let fakeTaskOpen1 = {
id: '1', name: 'FakeOpenTask1', description: null, category: null,
assignee: fakeUser1,
created: '2017-07-15T11:19:17.440+0000',
dueDate: null,
endDate: null
};
export let fakeTaskOpen2 = {
id: '1', name: 'FakeOpenTask2', description: null, category: null,
assignee: { id: 1, email: 'fake-open-email@dom.com', firstName: 'firstName', lastName: 'lastName' },
created: '2017-07-15T11:19:17.440+0000',
dueDate: null,
endDate: null
};
export let fakeTaskCompleted1 = {
id: '1', name: 'FakeCompletedTaskName1', description: null, category: null,
assignee: { id: 1, email: 'fake-completed-email@dom.com', firstName: 'firstName', lastName: 'lastName' },
created: '2016-07-15T11:19:17.440+0000',
dueDate: null,
endDate: '2016-11-03T15:25:42.749+0000'
};
export let fakeTaskCompleted2 = {
id: '1', name: 'FakeCompletedTaskName2', description: null, category: null,
assignee: fakeUser1,
created: null,
dueDate: null,
endDate: '2016-11-03T15:25:42.749+0000'
};
export let fakeOpenTaskList = {
size: 2,
total: 2,
start: 0,
data: [fakeTaskOpen1, fakeTaskOpen2]
};
export let fakeCompletedTaskList = {
size: 2,
total: 2,
start: 0,
data: [fakeTaskCompleted1, fakeTaskCompleted2]
};

View File

@@ -0,0 +1,52 @@
/*!
* @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.
*/
export class QueryModel {
processDefinitionId: string;
appName: string;
state: string;
sort: string;
assignment: string;
order: string;
constructor(obj?: any) {
if (obj) {
this.appName = obj.appName || null;
this.processDefinitionId = obj.processDefinitionId || null;
this.state = obj.state || null;
this.sort = obj.sort || null;
this.assignment = obj.assignment || null;
this.order = obj.order || null;
}
}
}
export class FilterRepresentationModel {
name: string;
icon: string;
query: QueryModel;
constructor(obj?: any) {
if (obj) {
this.name = obj.name || null;
this.icon = obj.icon || null;
this.query = new QueryModel(obj.query);
}
}
hasFilter() {
return !!this.query;
}
}

View File

@@ -15,4 +15,6 @@
* limitations under the License. * limitations under the License.
*/ */
export * from './index'; export * from './task-filters-cloud/task-filters-cloud.component';
export * from './models/filter-cloud.model';
export * from './task-cloud.module';

View File

@@ -0,0 +1,181 @@
/*!
* @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 { LogService, StorageService } from '@alfresco/adf-core';
import { Injectable } from '@angular/core';
import { Observable, forkJoin } from 'rxjs';
import { FilterRepresentationModel, QueryModel } from '../models/filter-cloud.model';
@Injectable()
export class TaskFilterCloudService {
constructor(private logService: LogService,
private storage: StorageService) {
}
/**
* Creates and returns the default filters for a process app.
* @param appName Name of the target app
* @returns Observable of default filters just created
*/
public createDefaultFilters(appName: string): Observable<FilterRepresentationModel[]> {
let involvedTasksFilter = this.getInvolvedTasksFilterInstance(appName);
let involvedObservable = this.addFilter(involvedTasksFilter);
let myTasksFilter = this.getMyTasksFilterInstance(appName);
let myTaskObservable = this.addFilter(myTasksFilter);
let queuedTasksFilter = this.getQueuedTasksFilterInstance(appName);
let queuedObservable = this.addFilter(queuedTasksFilter);
let completedTasksFilter = this.getCompletedTasksFilterInstance(appName);
let completeObservable = this.addFilter(completedTasksFilter);
return new Observable(observer => {
forkJoin(
involvedObservable,
myTaskObservable,
queuedObservable,
completeObservable
).subscribe(
(filters) => {
observer.next(filters);
observer.complete();
},
(err: any) => {
this.logService.error(err);
});
});
}
/**
* Gets all task filters for a process app.
* @param appName Name of the target app
* @returns Observable of task filter details
*/
getTaskListFilters(appName?: string): Observable<FilterRepresentationModel[]> {
let key = 'task-filters-' + appName;
const filters = JSON.parse(this.storage.getItem(key) || '[]');
return new Observable(function(observer) {
observer.next(filters);
observer.complete();
});
}
/**
* Adds a new task filter
* @param filter The new filter to add
* @returns Details of task filter just added
*/
addFilter(filter: FilterRepresentationModel): Observable<FilterRepresentationModel> {
const key = 'task-filters-' + filter.query.appName || '0';
let filters = JSON.parse(this.storage.getItem(key) || '[]');
filters.push(filter);
this.storage.setItem(key, JSON.stringify(filters));
return new Observable(function(observer) {
observer.next(filter);
observer.complete();
});
}
/**
* Creates and returns a filter for "Involved" task instances.
* @param appName Name of the target app
* @returns The newly created filter
*/
getInvolvedTasksFilterInstance(appName: string): FilterRepresentationModel {
return new FilterRepresentationModel({
name: 'Cancelled Tasks',
icon: 'view_headline',
query: new QueryModel(
{
appName: appName,
sort: 'id',
state: 'CANCELLED',
assignment: 'involved',
order: 'DESC'
}
)
});
}
/**
* Creates and returns a filter for "My Tasks" task instances.
* @param appName Name of the target app
* @returns The newly created filter
*/
getMyTasksFilterInstance(appName: string): FilterRepresentationModel {
return new FilterRepresentationModel({
name: 'My Tasks',
icon: 'inbox',
query: new QueryModel(
{
appName: appName,
sort: 'id',
state: 'CREATED',
assignment: 'assignee',
order: 'ASC'
}
)
});
}
/**
* Creates and returns a filter for "Queued Tasks" task instances.
* @param appName Name of the target app
* @returns The newly created filter
*/
getQueuedTasksFilterInstance(appName: string): FilterRepresentationModel {
return new FilterRepresentationModel({
name: 'Suspended Tasks',
icon: 'adjust',
query: new QueryModel(
{
appName: appName,
sort: 'createdDate',
state: 'SUSPENDED',
assignment: 'candidate',
order: 'DESC'
}
)
});
}
/**
* Creates and returns a filter for "Completed" task instances.
* @param appName Name of the target app
* @returns The newly created filter
*/
getCompletedTasksFilterInstance(appName: string): FilterRepresentationModel {
return new FilterRepresentationModel({
name: 'Completed Tasks',
icon: 'done',
query: new QueryModel(
{
appName: appName,
sort: 'createdDate',
state: 'COMPLETED',
assignment: 'involved',
order: 'ASC'
}
)
});
}
}

View File

@@ -0,0 +1,13 @@
import { TaskCloudModule } from './task-cloud.module';
describe('TaskCloudModule', () => {
let taskCloudModule: TaskCloudModule;
beforeEach(() => {
taskCloudModule = new TaskCloudModule();
});
it('should create an instance', () => {
expect(taskCloudModule).toBeTruthy();
});
});

View File

@@ -0,0 +1,26 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TaskFiltersCloudComponent } from './task-filters-cloud/task-filters-cloud.component';
import { MaterialModule } from '../material.module';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { TranslateLoaderService, LogService, StorageService } from '@alfresco/adf-core';
import { TaskFilterCloudService } from './services/task-filter-cloud.service';
import { HttpClientModule } from '@angular/common/http';
@NgModule({
imports: [
HttpClientModule,
CommonModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderService
}
}),
MaterialModule
],
declarations: [TaskFiltersCloudComponent],
exports: [TaskFiltersCloudComponent],
providers: [TaskFilterCloudService, LogService, StorageService]
})
export class TaskCloudModule { }

View File

@@ -0,0 +1,17 @@
<div class="menu-container">
<mat-list class="adf-menu-list" *ngIf="filters$ | async; else loading">
<mat-list-item (click)="selectFilterAndEmit(filter)" *ngFor="let filter of filters$ | async"
class="adf-filters__entry" [class.active]="currentFilter === filter">
<mat-icon *ngIf="showIcons && filter.icon" matListIcon class="adf-filters__entry-icon">{{filter.icon}}
</mat-icon>
<span matLine [attr.data-automation-id]="filter.name + '_filter'">{{filter.name}}</span>
</mat-list-item>
</mat-list>
<ng-template #loading>
<ng-container>
<div class="adf-app-list-spinner">
<mat-spinner></mat-spinner>
</div>
</ng-container>
</ng-template>
</div>

View File

@@ -0,0 +1,34 @@
@mixin adf-cloud-task-filters-theme($theme) {
$primary: map-get($theme, primary);
.adf {
&-filters__entry {
cursor: pointer;
font-size: 14px!important;
font-weight: bold;
opacity: .54;
padding-left: 30px;
.mat-list-item-content {
height: 34px;
}
}
&-filters__entry-icon {
padding-right: 12px !important;
padding-left: 0px !important;
}
&-filters__entry {
&.active, &:hover {
color: mat-color($primary);
opacity: 1;
}
}
&-menu-list {
padding-top: 0px!important;
}
}
}

View File

@@ -0,0 +1,359 @@
/*!
* @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 { SimpleChange } from '@angular/core';
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { setupTestBed } from '@alfresco/adf-core';
import { from, Observable } from 'rxjs';
import { FilterRepresentationModel } from '../models/filter-cloud.model';
import { TaskFilterCloudService } from '../services/task-filter-cloud.service';
import { TaskFiltersCloudComponent } from './task-filters-cloud.component';
import { By } from '@angular/platform-browser';
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
import { TaskCloudModule } from '../task-cloud.module';
describe('TaskFiltersCloudComponent', () => {
let taskFilterService: TaskFilterCloudService;
let fakeGlobalFilter = [
new FilterRepresentationModel({
name: 'FakeInvolvedTasks',
icon: 'adjust',
id: 10,
filter: {state: 'open', assignment: 'fake-involved'}
}),
new FilterRepresentationModel({
name: 'FakeMyTasks1',
icon: 'done',
id: 11,
filter: {state: 'open', assignment: 'fake-assignee'}
}),
new FilterRepresentationModel({
name: 'FakeMyTasks2',
icon: 'inbox',
id: 12,
filter: {state: 'open', assignment: 'fake-assignee'}
})
];
let fakeGlobalFilterObservable =
new Observable(function(observer) {
observer.next(fakeGlobalFilter);
observer.complete();
});
let fakeGlobalFilterPromise = new Promise(function (resolve, reject) {
resolve(fakeGlobalFilter);
});
let fakeGlobalEmptyFilter = {
message: 'invalid data'
};
let fakeGlobalEmptyFilterPromise = new Promise(function (resolve, reject) {
resolve(fakeGlobalEmptyFilter);
});
let mockErrorFilterList = {
error: 'wrong request'
};
let mockErrorFilterPromise = Promise.reject(mockErrorFilterList);
let component: TaskFiltersCloudComponent;
let fixture: ComponentFixture<TaskFiltersCloudComponent>;
setupTestBed({
imports: [ProcessServiceCloudTestingModule, TaskCloudModule],
providers: [TaskFilterCloudService]
});
beforeEach(() => {
fixture = TestBed.createComponent(TaskFiltersCloudComponent);
component = fixture.componentInstance;
taskFilterService = TestBed.get(TaskFilterCloudService);
});
it('should attach specific icon for each filter if hasIcon is true', async(() => {
spyOn(taskFilterService, 'getTaskListFilters').and.returnValue(fakeGlobalFilterObservable);
let change = new SimpleChange(undefined, 'my-app-1', true);
component.ngOnChanges({'appName': change});
fixture.detectChanges();
component.showIcons = true;
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(component.filters.length).toBe(3);
let filters = fixture.nativeElement.querySelectorAll('.adf-filters__entry-icon');
expect(filters.length).toBe(3);
expect(filters[0].innerText).toContain('adjust');
expect(filters[1].innerText).toContain('done');
expect(filters[2].innerText).toContain('inbox');
});
}));
it('should not attach icons for each filter if hasIcon is false', (done) => {
spyOn(taskFilterService, 'getTaskListFilters').and.returnValue(from(fakeGlobalFilterPromise));
component.showIcons = false;
let change = new SimpleChange(undefined, 'my-app-1', true);
component.ngOnChanges({'appName': change});
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
let filters: any = fixture.debugElement.queryAll(By.css('.adf-filters__entry-icon'));
expect(filters.length).toBe(0);
done();
});
});
it('should display the filters', async(() => {
spyOn(taskFilterService, 'getTaskListFilters').and.returnValue(fakeGlobalFilterObservable);
let change = new SimpleChange(undefined, 'my-app-1', true);
component.ngOnChanges({'appName': change});
fixture.detectChanges();
component.showIcons = true;
fixture.whenStable().then(() => {
fixture.detectChanges();
let filters = fixture.debugElement.queryAll(By.css('mat-list-item[class*="adf-filters__entry"]'));
expect(component.filters.length).toBe(3);
expect(filters.length).toBe(3);
expect(filters[0].nativeElement.innerText).toContain('FakeInvolvedTasks');
expect(filters[1].nativeElement.innerText).toContain('FakeMyTasks1');
expect(filters[2].nativeElement.innerText).toContain('FakeMyTasks2');
});
}));
it('should emit an error with a bad response', (done) => {
spyOn(taskFilterService, 'getTaskListFilters').and.returnValue(from(mockErrorFilterPromise));
const appName = 'my-app-1';
let change = new SimpleChange(null, appName, true);
component.ngOnChanges({'appName': change});
component.error.subscribe((err) => {
expect(err).toBeDefined();
done();
});
});
it('should return the filter task list', (done) => {
spyOn(taskFilterService, 'getTaskListFilters').and.returnValue(from(fakeGlobalFilterPromise));
const appName = 'my-app-1';
let change = new SimpleChange(null, appName, true);
component.ngOnChanges({ 'appName': change });
component.success.subscribe((res) => {
expect(res).toBeDefined();
expect(component.filters).toBeDefined();
expect(component.filters.length).toEqual(3);
done();
});
});
it('should return the filter task list, filtered By Name', (done) => {
spyOn(taskFilterService, 'getTaskListFilters').and.returnValue(from(fakeGlobalFilterPromise));
const appName = 'my-app-1';
let change = new SimpleChange(null, appName, true);
component.ngOnChanges({ 'appName': change });
component.success.subscribe((res) => {
expect(res).toBeDefined();
expect(component.filters).toBeDefined();
expect(component.filters[0].name).toEqual('FakeInvolvedTasks');
expect(component.filters[1].name).toEqual('FakeMyTasks1');
expect(component.filters[2].name).toEqual('FakeMyTasks2');
done();
});
});
it('should select the first filter as default', async(() => {
spyOn(taskFilterService, 'getTaskListFilters').and.returnValue(fakeGlobalFilterObservable);
const appName = 'my-app-1';
let change = new SimpleChange(null, appName, true);
fixture.detectChanges();
component.ngOnChanges({ 'appName': change });
component.success.subscribe((res) => {
expect(res).toBeDefined();
expect(component.currentFilter).toBeDefined();
expect(component.currentFilter.name).toEqual('FakeInvolvedTasks');
});
}));
it('should be able to fetch and select the default filters if the input filter is not valid', (done) => {
spyOn(taskFilterService, 'getTaskListFilters').and.returnValue(from(fakeGlobalEmptyFilterPromise));
spyOn(component, 'createFilters').and.callThrough();
const appName = 'my-app-1';
let change = new SimpleChange(null, appName, true);
component.ngOnChanges({ 'appName': change });
component.success.subscribe((res) => {
expect(res).toBeDefined();
expect(component.createFilters).not.toHaveBeenCalled();
done();
});
});
it('should select the task filter based on the input by name param', async(() => {
spyOn(taskFilterService, 'getTaskListFilters').and.returnValue(fakeGlobalFilterObservable);
component.filterParam = new FilterRepresentationModel({ name: 'FakeMyTasks1' });
const appName = 'my-app-1';
let change = new SimpleChange(null, appName, true);
fixture.detectChanges();
component.ngOnChanges({ 'appName': change });
component.success.subscribe((res) => {
expect(res).toBeDefined();
expect(component.currentFilter).toBeDefined();
expect(component.currentFilter.name).toEqual('FakeMyTasks1');
});
}));
it('should select the default task filter if filter input does not exist', async(() => {
spyOn(taskFilterService, 'getTaskListFilters').and.returnValue(fakeGlobalFilterObservable);
component.filterParam = new FilterRepresentationModel({ name: 'UnexistableFilter' });
const appName = 'my-app-1';
let change = new SimpleChange(null, appName, true);
fixture.detectChanges();
component.ngOnChanges({ 'appName': change });
component.success.subscribe((res) => {
expect(res).toBeDefined();
expect(component.currentFilter).toBeDefined();
expect(component.currentFilter.name).toEqual('FakeInvolvedTasks');
});
}));
it('should select the task filter based on the input by index param', async(() => {
spyOn(taskFilterService, 'getTaskListFilters').and.returnValue(fakeGlobalFilterObservable);
component.filterParam = new FilterRepresentationModel({ index: 2 });
const appName = 'my-app-1';
let change = new SimpleChange(null, appName, true);
fixture.detectChanges();
component.ngOnChanges({ 'appName': change });
component.success.subscribe((res) => {
expect(res).toBeDefined();
expect(component.currentFilter).toBeDefined();
expect(component.currentFilter.name).toEqual('FakeMyTasks2');
});
}));
it('should select the task filter based on the input by id param', async(() => {
spyOn(taskFilterService, 'getTaskListFilters').and.returnValue(fakeGlobalFilterObservable);
component.filterParam = new FilterRepresentationModel({ id: 12 });
const appName = 'my-app-1';
let change = new SimpleChange(null, appName, true);
fixture.detectChanges();
component.ngOnChanges({ 'appName': change });
component.success.subscribe((res) => {
expect(res).toBeDefined();
expect(component.currentFilter).toBeDefined();
expect(component.currentFilter.name).toEqual('FakeMyTasks2');
});
}));
it('should emit an event when a filter is selected', async(() => {
spyOn(taskFilterService, 'getTaskListFilters').and.returnValue(fakeGlobalFilterObservable);
component.filterParam = new FilterRepresentationModel({ id: 12 });
const appName = 'my-app-1';
let change = new SimpleChange(null, appName, true);
component.ngOnChanges({ 'appName': change });
fixture.detectChanges();
spyOn(component, 'selectFilterAndEmit').and.stub();
let filterButton = fixture.debugElement.nativeElement.querySelector('span[data-automation-id="FakeMyTasks1_filter"]');
filterButton.click();
expect(component.selectFilterAndEmit).toHaveBeenCalledWith(fakeGlobalFilter[1]);
}));
it('should reload filters by appName on binding changes', () => {
spyOn(component, 'getFilters').and.stub();
const appName = 'my-app-1';
let change = new SimpleChange(null, appName, true);
component.ngOnChanges({ 'appName': change });
expect(component.getFilters).toHaveBeenCalledWith(appName);
});
it('should not reload filters by appName null on binding changes', () => {
spyOn(component, 'getFilters').and.stub();
const appName = null;
let change = new SimpleChange(undefined, appName, true);
component.ngOnChanges({ 'appName': change });
expect(component.getFilters).not.toHaveBeenCalledWith(appName);
});
it('should change current filter when filterParam (name) changes', () => {
component.filters = fakeGlobalFilter;
component.currentFilter = null;
fixture.whenStable().then(() => {
expect(component.currentFilter.name).toEqual(fakeGlobalFilter[2].name);
});
const change = new SimpleChange(null, { name: fakeGlobalFilter[2].name }, true);
component.ngOnChanges({ 'filterParam': change });
});
it('should reload filters by app name on binding changes', () => {
spyOn(component, 'getFilters').and.stub();
const appName = 'fake-app-name';
let change = new SimpleChange(null, appName, true);
component.ngOnChanges({ 'appName': change });
expect(component.getFilters).toHaveBeenCalledWith(appName);
});
it('should return the current filter after one is selected', () => {
let filter = fakeGlobalFilter[1];
component.filters = fakeGlobalFilter;
expect(component.currentFilter).toBeUndefined();
component.selectFilter(filter);
expect(component.getCurrentFilter()).toBe(filter);
});
});

View File

@@ -0,0 +1,156 @@
/*!
* @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 { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import { Observable } from 'rxjs';
import { TaskFilterCloudService } from '../services/task-filter-cloud.service';
import { FilterRepresentationModel } from '../models/filter-cloud.model';
@Component({
selector: 'adf-cloud-task-filters',
templateUrl: './task-filters-cloud.component.html',
styleUrls: ['task-filters-cloud.component.scss']
})
export class TaskFiltersCloudComponent implements OnChanges {
@Input()
appName: string;
@Input()
filterParam: FilterRepresentationModel;
@Input()
showIcons: boolean = false;
@Output()
filterClick: EventEmitter<FilterRepresentationModel> = new EventEmitter<FilterRepresentationModel>();
@Output()
success: EventEmitter<any> = new EventEmitter<any>();
@Output()
error: EventEmitter<any> = new EventEmitter<any>();
filters$: Observable<FilterRepresentationModel[]>;
currentFilter: FilterRepresentationModel;
filters: FilterRepresentationModel [] = [];
constructor(private taskFilterCloudService: TaskFilterCloudService) {
}
ngOnChanges(changes: SimpleChanges) {
const appName = changes['appName'];
const filter = changes['filterParam'];
if (appName && appName.currentValue) {
this.getFilters(appName.currentValue);
} else if (filter && filter.currentValue !== filter.previousValue) {
this.selectFilter(filter.currentValue);
}
}
/**
* Return the filter list filtered by appName
*/
getFilters(appName: string) {
this.filters$ = this.taskFilterCloudService.getTaskListFilters(appName);
this.filters$.subscribe(
(res: FilterRepresentationModel[]) => {
if (res.length === 0) {
this.createFilters(appName);
} else {
this.resetFilter();
this.filters = res;
}
this.selectFilterAndEmit(this.filterParam);
this.success.emit(res);
},
(err: any) => {
this.error.emit(err);
}
);
}
/**
* Create default filters by appId
*/
createFilters(appName?: string) {
this.filters$ = this.taskFilterCloudService.createDefaultFilters(appName);
this.filters$.subscribe(
(resDefault: FilterRepresentationModel[]) => {
this.resetFilter();
this.filters = resDefault;
},
(errDefault: any) => {
this.error.emit(errDefault);
}
);
}
/**
* Pass the selected filter as next
*/
public selectFilter(newFilter: FilterRepresentationModel) {
if (newFilter) {
this.currentFilter = this.filters.find((filter) =>
(newFilter.name &&
(newFilter.name.toLocaleLowerCase() === filter.name.toLocaleLowerCase())
));
}
if (!this.currentFilter) {
this.selectDefaultTaskFilter();
}
}
public selectFilterAndEmit(newFilter: FilterRepresentationModel) {
this.selectFilter(newFilter);
this.filterClick.emit(this.currentFilter);
}
/**
* Select as default task filter the first in the list
*/
public selectDefaultTaskFilter() {
if (!this.isFilterListEmpty()) {
this.currentFilter = this.filters[0];
}
}
/**
* Return the current task
*/
getCurrentFilter(): FilterRepresentationModel {
return this.currentFilter;
}
/**
* Check if the filter list is empty
*/
isFilterListEmpty(): boolean {
return this.filters === undefined || (this.filters && this.filters.length === 0);
}
/**
* Reset the filters properties
*/
private resetFilter() {
this.filters = [];
this.currentFilter = undefined;
}
}

View File

@@ -0,0 +1,4 @@
.adf-cloud-task-list-loading-margin {
margin-left: calc((100% - 100px) / 2);
margin-right: calc((100% - 100px) / 2);
}

View File

@@ -0,0 +1,33 @@
<div *ngIf="!rows">{{ 'ADF_TASK_LIST.FILTERS.MESSAGES.NONE' | translate }}</div>
<ng-container *ngIf="rows">
<adf-datatable
[rows]="rows"
[columns]="columns"
[loading]="isLoading"
[multiselect]="multiselect"
[selectionMode]="selectionMode"
(row-select)="onRowSelect($event)"
(row-unselect)="onRowUnselect($event)"
(rowClick)="onRowClick($event)"
(row-keyup)="onRowKeyUp($event)">
<loading-content-template>
<ng-template>
<!-- Add your custom loading template here -->
<mat-progress-spinner class="adf-cloud-task-list-loading-margin"
[color]="'primary'"
[mode]="'indeterminate'">
</mat-progress-spinner>
</ng-template>
</loading-content-template>
<no-content-template>
<ng-template>
<adf-empty-content *ngIf="!emptyCustomContent"
icon="assignment"
[title]="'ADF_TASK_LIST.LIST.MESSAGES.TITLE' | translate"
[subtitle]="'ADF_TASK_LIST.LIST.MESSAGES.SUBTITLE' | translate">
</adf-empty-content>
<ng-content select="adf-empty-custom-content"></ng-content>
</ng-template>
</no-content-template>
</adf-datatable>
</ng-container>

View File

@@ -0,0 +1,312 @@
/*!
* @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 { Component, SimpleChange, ViewChild, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { AppConfigService, setupTestBed, CoreModule } from '@alfresco/adf-core';
import { DataRowEvent, ObjectDataRow } from '@alfresco/adf-core';
import { TaskListCloudService } from '../services/task-list-cloud.service';
import { TaskListCloudComponent } from './task-list-cloud.component';
import { fakeGlobalTask, fakeCustomSchema, fakeTaskCloudList } from '../mock/fakeTaskResponseMock';
import { of } from 'rxjs';
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
import { TaskListCloudModule } from '../task-list-cloud.module';
@Component({
template: `
<adf-cloud-task-list #taskListCloud>
<data-columns>
<data-column key="name" title="ADF_TASK_LIST.PROPERTIES.NAME" class="full-width name-column"></data-column>
<data-column key="created" title="ADF_TASK_LIST.PROPERTIES.CREATED" class="hidden"></data-column>
<data-column key="startedBy" title="ADF_TASK_LIST.PROPERTIES.CREATED" class="desktop-only dw-dt-col-3 ellipsis-cell">
<ng-template let-entry="$implicit">
<div>{{getFullName(entry.row.obj.startedBy)}}</div>
</ng-template>
</data-column>
</data-columns>
</adf-cloud-task-list>`
})
class CustomTaskListComponent {
@ViewChild(TaskListCloudComponent)
taskList: TaskListCloudComponent;
}
@Component({
template: `
<adf-tasklist>
<adf-empty-content-holder>
<p id="custom-id"></p>
</adf-empty-content-holder>
</adf-tasklist>
`
})
class EmptyTemplateComponent {
}
describe('TaskListCloudComponent', () => {
let component: TaskListCloudComponent;
let fixture: ComponentFixture<TaskListCloudComponent>;
let appConfig: AppConfigService;
let taskListCloudService: TaskListCloudService;
setupTestBed({
imports: [
ProcessServiceCloudTestingModule, TaskListCloudModule
],
providers: [TaskListCloudService]
});
beforeEach(() => {
appConfig = TestBed.get(AppConfigService);
taskListCloudService = TestBed.get(TaskListCloudService);
fixture = TestBed.createComponent(TaskListCloudComponent);
component = fixture.componentInstance;
appConfig.config = Object.assign(appConfig.config, {
'adf-cloud-task-list': {
'presets': {
'fakeCustomSchema': [
{
'key': 'fakeName',
'type': 'text',
'title': 'ADF_TASK_LIST.PROPERTIES.FAKE',
'sortable': true
},
{
'key': 'fakeTaskName',
'type': 'text',
'title': 'ADF_TASK_LIST.PROPERTIES.TASK_FAKE',
'sortable': true
}
]
}
}
});
});
afterEach(() => {
fixture.destroy();
});
it('should use the default schemaColumn as default', () => {
component.ngAfterContentInit();
expect(component.columns).toBeDefined();
expect(component.columns.length).toEqual(3);
});
it('should use the custom schemaColumn from app.config.json', () => {
component.presetColumn = 'fakeCustomSchema';
component.ngAfterContentInit();
fixture.detectChanges();
expect(component.columns).toEqual(fakeCustomSchema);
});
it('should fetch custom schemaColumn when the input presetColumn is defined', () => {
component.presetColumn = 'fakeCustomSchema';
fixture.detectChanges();
expect(component.columns).toBeDefined();
expect(component.columns.length).toEqual(2);
});
it('should return an empty task list when no input parameters are passed', () => {
component.ngAfterContentInit();
expect(component.rows).toBeDefined();
expect(component.isListEmpty()).toBeTruthy();
});
it('should return the results if an application name is given', (done) => {
spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(fakeGlobalTask));
let appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
component.success.subscribe((res) => {
expect(res).toBeDefined();
expect(component.rows).toBeDefined();
expect(component.isListEmpty()).not.toBeTruthy();
expect(component.rows.length).toEqual(1);
expect(component.rows[0].entry['serviceName']).toEqual('test-ciprian2-rb');
expect(component.rows[0].entry['serviceFullName']).toEqual('test-ciprian2-rb');
expect(component.rows[0].entry['serviceVersion']).toBe('');
expect(component.rows[0].entry['appName']).toBe('test-ciprian2');
expect(component.rows[0].entry['appVersion']).toBe('');
expect(component.rows[0].entry['serviceType']).toBeNull();
expect(component.rows[0].entry['id']).toBe('11fe013d-c263-11e8-b75b-0a5864600540');
expect(component.rows[0].entry['assignee']).toBeNull();
expect(component.rows[0].entry['name']).toEqual('standalone-subtask');
expect(component.rows[0].entry['description']).toBeNull();
expect(component.rows[0].entry['createdDate']).toBe(1538059139420);
expect(component.rows[0].entry['dueDate']).toBeNull();
expect(component.rows[0].entry['claimedDate']).toBeNull();
expect(component.rows[0].entry['priority']).toBe(0);
expect(component.rows[0].entry['category']).toBeNull();
expect(component.rows[0].entry['processDefinitionId']).toBeNull();
expect(component.rows[0].entry['processInstanceId']).toBeNull();
expect(component.rows[0].entry['status']).toBe('CREATED');
expect(component.rows[0].entry['owner']).toBe('devopsuser');
expect(component.rows[0].entry['parentTaskId']).toBe('71fda20b-c25b-11e8-b75b-0a5864600540');
expect(component.rows[0].entry['lastModified']).toBe(1538059139420);
expect(component.rows[0].entry['lastModifiedTo']).toBeNull();
expect(component.rows[0].entry['lastModifiedFrom']).toBeNull();
expect(component.rows[0].entry['standAlone']).toBeTruthy();
done();
});
component.applicationName = appName.currentValue;
component.ngOnChanges({ 'appName': appName });
fixture.detectChanges();
});
it('should return a currentId null when the taskList is empty', () => {
component.selectTask(null);
expect(component.getCurrentId()).toBeNull();
});
it('should return selected id for the selected task', () => {
component.rows = [
{ entry: { id: '999', name: 'Fake-name' } },
{ entry: { id: '888', name: 'Fake-name-888' } }
];
component.selectTask('888');
expect(component.rows).toBeDefined();
expect(component.currentInstanceId).toEqual('888');
});
it('should reload tasks when reload() is called', (done) => {
component.applicationName = 'fake';
spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(fakeGlobalTask));
component.success.subscribe((res) => {
expect(res).toBeDefined();
expect(component.rows).toBeDefined();
expect(component.isListEmpty()).not.toBeTruthy();
done();
});
fixture.detectChanges();
component.reload();
});
it('should emit row click event', (done) => {
let row = new ObjectDataRow({
entry: {
id: '999'
}
});
let rowEvent = new DataRowEvent(row, null);
component.rowClick.subscribe(taskId => {
expect(taskId).toEqual('999');
expect(component.getCurrentId()).toEqual('999');
done();
});
component.onRowClick(rowEvent);
});
describe('component changes', () => {
beforeEach(() => {
component.rows = fakeGlobalTask.list.entries;
fixture.detectChanges();
});
it('should NOT reload the tasks if the landingTaskId is the same of the current task', () => {
spyOn(component, 'reload').and.stub();
component.currentInstanceId = '999';
component.rows = [{ entry: { id: '999', name: 'Fake-name' } }];
const landingTaskId = '999';
let change = new SimpleChange('999', landingTaskId, true);
component.ngOnChanges({ 'landingTaskId': change });
expect(component.reload).not.toHaveBeenCalled();
expect(component.rows.length).toEqual(1);
});
it('should reload the tasks if the loadingTaskId is different from the current task', (done) => {
component.currentInstanceId = '999';
component.rows = [{ id: '999', name: 'Fake-name' }];
const landingTaskId = '888';
let change = new SimpleChange(null, landingTaskId, true);
component.applicationName = 'fake';
spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(fakeTaskCloudList));
component.success.subscribe((res) => {
expect(res).toBeDefined();
expect(component.rows).toBeDefined();
expect(component.rows.length).toEqual(2);
done();
});
component.ngOnChanges({ 'landingTaskId': change });
});
it('should NOT reload the task list when no parameters changed', () => {
component.rows = null;
component.ngOnChanges({});
fixture.detectChanges();
expect(component.isListEmpty()).toBeTruthy();
});
});
describe('Injecting custom colums for tasklist - CustomTaskListComponent', () => {
let fixtureCustom: ComponentFixture<CustomTaskListComponent>;
let componentCustom: CustomTaskListComponent;
setupTestBed({
imports: [CoreModule.forRoot()],
declarations: [TaskListCloudComponent, CustomTaskListComponent],
providers: [TaskListCloudService]
});
beforeEach(() => {
fixtureCustom = TestBed.createComponent(CustomTaskListComponent);
fixtureCustom.detectChanges();
componentCustom = fixtureCustom.componentInstance;
});
afterEach(() => {
fixtureCustom.destroy();
});
it('should create instance of CustomTaskListComponent', () => {
expect(componentCustom instanceof CustomTaskListComponent).toBe(true, 'should create CustomTaskListComponent');
});
it('should fetch custom schemaColumn from html', () => {
fixture.detectChanges();
expect(componentCustom.taskList.columnList).toBeDefined();
expect(componentCustom.taskList.columns[0]['title']).toEqual('ADF_TASK_LIST.PROPERTIES.NAME');
expect(componentCustom.taskList.columns[1]['title']).toEqual('ADF_TASK_LIST.PROPERTIES.CREATED');
expect(componentCustom.taskList.columns.length).toEqual(3);
});
});
describe('Creating an empty custom template - EmptyTemplateComponent', () => {
let fixtureEmpty: ComponentFixture<EmptyTemplateComponent>;
setupTestBed({
imports: [ProcessServiceCloudTestingModule, TaskListCloudModule],
declarations: [EmptyTemplateComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
});
beforeEach(() => {
fixtureEmpty = TestBed.createComponent(EmptyTemplateComponent);
fixtureEmpty.detectChanges();
});
afterEach(() => {
fixtureEmpty.destroy();
});
it('should render the custom template', async(() => {
fixtureEmpty.whenStable().then(() => {
fixtureEmpty.detectChanges();
expect(fixtureEmpty.debugElement.query(By.css('#custom-id'))).not.toBeNull();
expect(fixtureEmpty.debugElement.query(By.css('.adf-empty-content'))).toBeNull();
});
}));
});
});

View File

@@ -0,0 +1,260 @@
/*!
* @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 { Component, ViewEncapsulation, OnChanges, Input, SimpleChanges, Output, EventEmitter, ContentChild, AfterContentInit, SimpleChange } from '@angular/core';
import { AppConfigService, UserPreferencesService,
DataTableSchema, UserPreferenceValues,
PaginatedComponent, PaginationModel,
DataRowEvent, EmptyCustomContentDirective } from '@alfresco/adf-core';
import { taskPresetsCloudDefaultModel } from '../models/task-preset-cloud.model';
import { TaskQueryCloudRequestModel } from '../models/filter-cloud-model';
import { BehaviorSubject } from 'rxjs';
import { TaskListCloudService } from '../services/task-list-cloud.service';
import { MinimalNodeEntity } from 'alfresco-js-api';
import { TaskListCloudSortingModel } from '../models/task-list-sorting.model';
@Component({
selector: 'adf-cloud-task-list',
templateUrl: './task-list-cloud.component.html',
styleUrls: ['./task-list-cloud.component.css'],
encapsulation: ViewEncapsulation.None
})
export class TaskListCloudComponent extends DataTableSchema implements OnChanges, AfterContentInit, PaginatedComponent {
static PRESET_KEY = 'adf-cloud-task-list.presets';
@ContentChild(EmptyCustomContentDirective)
emptyCustomContent: EmptyCustomContentDirective;
@Input()
applicationName: string = '';
@Input()
assignee: string = '';
@Input()
createdDate: string = '';
@Input()
dueDate: string = '';
@Input()
id: string = '';
@Input()
name: string = '';
@Input()
parentTaskId: string = '';
@Input()
processDefinitionId: string = '';
@Input()
processInstanceId: string = '';
@Input()
status: string = '';
@Input()
selectFirstRow: boolean = true;
@Input()
landingTaskId: string;
@Input()
selectionMode: string = 'single'; // none|single|multiple
/** Toggles multiple row selection, renders checkboxes at the beginning of each row */
@Input()
multiselect: boolean = false;
@Input()
sorting: TaskListCloudSortingModel[];
/** Emitted when a task in the list is clicked */
@Output()
rowClick: EventEmitter<string> = new EventEmitter<string>();
/** Emitted when rows are selected/unselected */
@Output()
rowsSelected: EventEmitter<any[]> = new EventEmitter<any[]>();
/** Emitted when the task list is loaded */
@Output()
success: EventEmitter<any> = new EventEmitter<any>();
/** Emitted when an error occurs. */
@Output()
error: EventEmitter<any> = new EventEmitter<any>();
pagination: BehaviorSubject<PaginationModel>;
requestNode: TaskQueryCloudRequestModel;
rows: any[] = [];
size: number;
skipCount: number = 0;
currentInstanceId: any;
isLoading = false;
selectedInstances: any[];
constructor(private taskListCloudService: TaskListCloudService,
appConfigService: AppConfigService,
private userPreferences: UserPreferencesService) {
super(appConfigService, TaskListCloudComponent.PRESET_KEY, taskPresetsCloudDefaultModel);
this.size = userPreferences.paginationSize;
this.userPreferences.select(UserPreferenceValues.PaginationSize).subscribe((pageSize) => {
this.size = pageSize;
});
this.pagination = new BehaviorSubject<PaginationModel>(<PaginationModel> {
maxItems: this.size,
skipCount: 0,
totalItems: 0
});
}
ngOnChanges(changes: SimpleChanges) {
if (this.isPropertyChanged(changes) &&
!this.isEqualToCurrentId(changes['landingTaskId'])) {
this.reload();
}
}
ngAfterContentInit() {
this.createDatatableSchema();
}
getCurrentId(): string {
return this.currentInstanceId;
}
isEqualToCurrentId(landingTaskChanged: SimpleChange): boolean {
return landingTaskChanged && this.currentInstanceId === landingTaskChanged.currentValue;
}
private isPropertyChanged(changes: SimpleChanges): boolean {
for (let property in changes) {
if (changes.hasOwnProperty(property)) {
if (changes[property] &&
(changes[property].currentValue !== changes[property].previousValue)) {
return true;
}
}
}
return false;
}
reload() {
this.requestNode = this.createRequestNode();
if (this.requestNode.appName) {
this.load(this.requestNode);
} else {
this.rows = [];
}
}
private load(requestNode: TaskQueryCloudRequestModel) {
this.isLoading = true;
this.taskListCloudService.getTaskByRequest(requestNode).subscribe(
(tasks) => {
this.rows = tasks.list.entries;
this.selectTask(this.landingTaskId);
this.success.emit(tasks);
this.isLoading = false;
this.pagination.next(tasks.list.pagination);
}, (error) => {
this.error.emit(error);
this.isLoading = false;
});
}
selectTask(taskIdSelected: string) {
if (!this.isListEmpty()) {
let dataRow: any = null;
if (taskIdSelected) {
dataRow = this.rows.find((currentRow: MinimalNodeEntity) => {
return currentRow.entry.id === taskIdSelected;
});
}
if (!dataRow && this.selectFirstRow) {
dataRow = this.rows[0];
}
if (dataRow) {
dataRow.isSelected = true;
this.currentInstanceId = dataRow.entry.id;
}
} else {
this.currentInstanceId = null;
}
}
isListEmpty(): boolean {
return !this.rows || this.rows.length === 0;
}
updatePagination(pagination: PaginationModel) {
this.size = pagination.maxItems;
this.skipCount = pagination.skipCount;
this.pagination.next(pagination);
this.reload();
}
onRowClick(item: DataRowEvent) {
this.currentInstanceId = item.value.getValue('entry.id');
this.rowClick.emit(this.currentInstanceId);
}
onRowSelect(event: CustomEvent) {
this.selectedInstances = [...event.detail.selection];
this.rowsSelected.emit(this.selectedInstances);
}
onRowUnselect(event: CustomEvent) {
this.selectedInstances = [...event.detail.selection];
this.rowsSelected.emit(this.selectedInstances);
}
onRowKeyUp(event: CustomEvent) {
if (event.detail.keyboardEvent.key === 'Enter') {
event.preventDefault();
this.currentInstanceId = event.detail.row.getValue('entry.id');
this.rowClick.emit(this.currentInstanceId);
}
}
private createRequestNode() {
let requestNode = {
appName: this.applicationName,
assignee: this.assignee,
id: this.id,
name: this.name,
parentTaskId: this.parentTaskId,
processDefinitionId: this.processDefinitionId,
processInstanceId: this.processInstanceId,
status: this.status,
maxItems: this.size,
skipCount: this.skipCount,
sorting: this.sorting
};
return new TaskQueryCloudRequestModel(requestNode);
}
}

View File

@@ -0,0 +1,145 @@
/*!
* @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 { ObjectDataColumn } from '@alfresco/adf-core';
export const fakeTaskCloudList = {
list: {
entries: [
{
entry: {
serviceName: 'maurizio-test-rb',
serviceFullName: 'maurizio-test-rb',
serviceVersion: '',
appName: 'save-the-cheerleader',
appVersion: '',
serviceType: null,
id: '890b0e1c-c252-11e8-b5c5-0a58646004c7',
assignee: null,
name: 'SimpleStandaloneTask',
description: 'Task description',
createdDate: 1538052037711,
dueDate: null,
claimedDate: null,
priority: 0,
category: null,
processDefinitionId: null,
processInstanceId: null,
status: 'CREATED',
owner: 'superadminuser',
parentTaskId: null,
lastModified: 1538052037711,
lastModifiedTo: null,
lastModifiedFrom: null,
standAlone: true
}
},
{
entry: {
serviceName: 'maurizio-test-rb',
serviceFullName: 'maurizio-test-rb',
serviceVersion: '',
appName: 'save-the-cheerleader',
appVersion: '',
serviceType: null,
id: '8962cb0e-c252-11e8-b5c5-0a58646004c7',
assignee: null,
name: 'SimpleStandaloneTask',
description: 'Task description',
createdDate: 1538052038286,
dueDate: null,
claimedDate: null,
priority: 0,
category: null,
processDefinitionId: null,
processInstanceId: null,
status: 'CREATED',
owner: 'superadminuser',
parentTaskId: null,
lastModified: 1538052038286,
lastModifiedTo: null,
lastModifiedFrom: null,
standAlone: true
}
}
],
pagination: {
skipCount: 0,
maxItems: 100,
count: 2,
hasMoreItems: false,
totalItems: 2
}
}
};
export let fakeGlobalTask = {
list: {
entries: [
{
entry: {
serviceName: 'test-ciprian2-rb',
serviceFullName: 'test-ciprian2-rb',
serviceVersion: '',
appName: 'test-ciprian2',
appVersion: '',
serviceType: null,
id: '11fe013d-c263-11e8-b75b-0a5864600540',
assignee: null,
name: 'standalone-subtask',
description: null,
createdDate: 1538059139420,
dueDate: null,
claimedDate: null,
priority: 0,
category: null,
processDefinitionId: null,
processInstanceId: null,
status: 'CREATED',
owner: 'devopsuser',
parentTaskId: '71fda20b-c25b-11e8-b75b-0a5864600540',
lastModified: 1538059139420,
lastModifiedTo: null,
lastModifiedFrom: null,
standAlone: true
}
}
],
pagination: {
skipCount: 0,
maxItems: 100,
count: 1,
hasMoreItems: false,
totalItems: 1
}
}
};
export let fakeCustomSchema =
[
new ObjectDataColumn({
'key': 'fakeName',
'type': 'text',
'title': 'ADF_TASK_LIST.PROPERTIES.FAKE',
'sortable': true
}),
new ObjectDataColumn({
'key': 'fakeTaskName',
'type': 'text',
'title': 'ADF_TASK_LIST.PROPERTIES.TASK_FAKE',
'sortable': true
})
];

View File

@@ -0,0 +1,70 @@
/*!
* @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 { TaskListCloudSortingModel } from './task-list-sorting.model';
export class TaskQueryCloudRequestModel {
appName: string;
appVersion?: string;
assignee?: string;
claimedDate?: string;
createdDate?: Date;
description?: string;
dueDate?: null;
id?: string;
name?: string;
owner?: string;
parentTaskId?: string;
priority?: number;
processDefinitionId?: string;
processInstanceId?: string;
serviceFullName?: string;
serviceName?: string;
serviceType?: string;
serviceVersion?: string;
status?: string;
maxItems: number;
skipCount: number;
sorting?: TaskListCloudSortingModel[];
constructor(obj?: any) {
if (obj) {
this.appName = obj.appName;
this.appVersion = obj.appVersion;
this.assignee = obj.assignee;
this.claimedDate = obj.claimedDate;
this.createdDate = obj.createdDate;
this.description = obj.description;
this.dueDate = obj.dueDate;
this.id = obj.id;
this.name = obj.name;
this.owner = obj.owner;
this.parentTaskId = obj.parentTaskId;
this.priority = obj.priority;
this.processDefinitionId = obj.processDefinitionId;
this.processInstanceId = obj.processInstanceId;
this.serviceFullName = obj.serviceFullName;
this.serviceName = obj.serviceName;
this.serviceType = obj.serviceType;
this.serviceVersion = obj.serviceVersion;
this.status = obj.status;
this.maxItems = obj.maxItems;
this.skipCount = obj.skipCount;
this.sorting = obj.sorting;
}
}
}

View File

@@ -0,0 +1,28 @@
/*!
* @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.
*/
export class TaskListCloudSortingModel {
orderBy: string;
direction: string;
constructor(obj: any) {
if (obj) {
this.orderBy = obj.orderBy;
this.direction = obj.direction;
}
}
}

View File

@@ -0,0 +1,41 @@
/*!
* @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.
*/
export let taskPresetsCloudDefaultModel = {
'default': [
{
'key': 'name',
'type': 'text',
'title': 'ADF_TASK_LIST.PROPERTIES.NAME',
'sortable': true
},
{
'key': 'created',
'type': 'text',
'title': 'ADF_TASK_LIST.PROPERTIES.CREATED',
'cssClass': 'hidden',
'sortable': true
},
{
'key': 'assignee',
'type': 'text',
'title': 'ADF_TASK_LIST.PROPERTIES.ASSIGNEE',
'cssClass': 'hidden',
'sortable': true
}
]
};

View File

@@ -0,0 +1,24 @@
/*!
* @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.
*/
export * from './components/task-list-cloud.component';
export * from './models/filter-cloud-model';
export * from './models/task-list-sorting.model';
export * from './models/task-preset-cloud.model';
export * from './task-list-cloud.module';

View File

@@ -0,0 +1,134 @@
/*!
* @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 { async } from '@angular/core/testing';
import { setupTestBed } from '@alfresco/adf-core';
import { fakeTaskCloudList } from '../mock/fakeTaskResponseMock';
import { AlfrescoApiServiceMock, LogService, AppConfigService, StorageService, CoreModule } from '@alfresco/adf-core';
import { TaskListCloudService } from './task-list-cloud.service';
import { TaskQueryCloudRequestModel } from '../models/filter-cloud-model';
describe('Activiti TaskList Cloud Service', () => {
let service: TaskListCloudService;
let alfrescoApiMock: AlfrescoApiServiceMock;
function returFakeTaskListResults() {
return {
oauth2Auth: {
callCustomApi : () => {
return Promise.resolve(fakeTaskCloudList);
}
}
};
}
function returnCallQueryParameters() {
return {
oauth2Auth: {
callCustomApi : (queryUrl, operation, context, queryParams) => {
return Promise.resolve(queryParams);
}
}
};
}
function returnCallUrl() {
return {
oauth2Auth: {
callCustomApi : (queryUrl, operation, context, queryParams) => {
return Promise.resolve(queryUrl);
}
}
};
}
setupTestBed({
imports: [
CoreModule.forRoot()
]
});
beforeEach(async(() => {
alfrescoApiMock = new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService() );
service = new TaskListCloudService(alfrescoApiMock,
new AppConfigService(null),
new LogService(new AppConfigService(null)));
}));
it('should return the tasks', (done) => {
let taskRequest: TaskQueryCloudRequestModel = <TaskQueryCloudRequestModel> { appName: 'fakeName' };
spyOn(alfrescoApiMock, 'getInstance').and.callFake(returFakeTaskListResults);
service.getTaskByRequest(taskRequest).subscribe((res) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
expect(res.list.entries.length).toBe(2);
expect(res.list.entries[0].entry.appName).toBe('save-the-cheerleader');
expect(res.list.entries[1].entry.appName).toBe('save-the-cheerleader');
done();
});
});
it('should append to the call all the parameters', (done) => {
let taskRequest: TaskQueryCloudRequestModel = <TaskQueryCloudRequestModel> { appName: 'fakeName', skipCount: 0, maxItems: 20, service: 'fake-service' };
spyOn(alfrescoApiMock, 'getInstance').and.callFake(returnCallQueryParameters);
service.getTaskByRequest(taskRequest).subscribe((res) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
expect(res.skipCount).toBe(0);
expect(res.maxItems).toBe(20);
expect(res.service).toBe('fake-service');
done();
});
});
it('should concat the app name to the request url', (done) => {
let taskRequest: TaskQueryCloudRequestModel = <TaskQueryCloudRequestModel> { appName: 'fakeName', skipCount: 0, maxItems: 20, service: 'fake-service' };
spyOn(alfrescoApiMock, 'getInstance').and.callFake(returnCallUrl);
service.getTaskByRequest(taskRequest).subscribe((requestUrl) => {
expect(requestUrl).toBeDefined();
expect(requestUrl).not.toBeNull();
expect(requestUrl).toContain('/fakeName-query/v1/tasks');
done();
});
});
it('should concat the sorting to append as parameters', (done) => {
let taskRequest: TaskQueryCloudRequestModel = <TaskQueryCloudRequestModel> { appName: 'fakeName', skipCount: 0, maxItems: 20, service: 'fake-service',
sorting: [{ orderBy: 'NAME', direction: 'DESC'}, { orderBy: 'TITLE', direction: 'ASC'}] };
spyOn(alfrescoApiMock, 'getInstance').and.callFake(returnCallQueryParameters);
service.getTaskByRequest(taskRequest).subscribe((res) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
expect(res.sort).toBe('NAME,DESC&TITLE,ASC');
done();
});
});
it('should return an error when app name is not specified', (done) => {
let taskRequest: TaskQueryCloudRequestModel = <TaskQueryCloudRequestModel> { appName: null };
spyOn(alfrescoApiMock, 'getInstance').and.callFake(returnCallUrl);
service.getTaskByRequest(taskRequest).subscribe(
() => { },
(error) => {
expect(error).toBe('Appname not configured');
done();
}
);
});
});

View File

@@ -0,0 +1,92 @@
/*!
* @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 { Injectable } from '@angular/core';
import { AlfrescoApiService, AppConfigService, LogService } from '@alfresco/adf-core';
import { TaskQueryCloudRequestModel } from '../models/filter-cloud-model';
import { Observable, from, throwError } from 'rxjs';
import { TaskListCloudSortingModel } from '../models/task-list-sorting.model';
@Injectable()
export class TaskListCloudService {
constructor(private apiService: AlfrescoApiService,
private appConfigService: AppConfigService,
private logService: LogService) {
}
contentTypes = ['application/json'];
accepts = ['application/json'];
getTaskByRequest(requestNode: TaskQueryCloudRequestModel): Observable<any> {
if (requestNode.appName) {
let queryUrl = this.buildQueryUrl(requestNode);
let queryParams = this.buildQueryParams(requestNode);
let sortingParams = this.buildSortingParam(requestNode.sorting);
if (sortingParams) {
queryParams['sort'] = sortingParams;
}
return from(this.apiService.getInstance()
.oauth2Auth.callCustomApi(queryUrl, 'GET',
null, queryParams, null,
null, null, null, ['application/json'],
['application/json'], Object, null, null)
);
} else {
this.logService.error('Appname is mandatory for querying task');
return throwError('Appname not configured');
}
}
private buildQueryUrl(requestNode: TaskQueryCloudRequestModel) {
return `${this.appConfigService.get('bpmHost', '')}/${requestNode.appName}-query/v1/tasks`;
}
private buildQueryParams(requestNode: TaskQueryCloudRequestModel) {
let queryParam = {};
for (let property in requestNode) {
if (requestNode.hasOwnProperty(property) &&
!this.isExcludedField(property) &&
this.isPropertyValueValid(requestNode, property)) {
queryParam[property] = requestNode[property];
}
}
return queryParam;
}
private isExcludedField(property) {
return property === 'appName' || property === 'sorting';
}
private isPropertyValueValid(requestNode, property) {
return requestNode[property] !== '' && requestNode[property] !== null && requestNode[property] !== undefined;
}
private buildSortingParam(sortings: TaskListCloudSortingModel[]): string {
let finalSorting: string = '';
if (sortings) {
for (let sort of sortings) {
if (!finalSorting) {
finalSorting = `${sort.orderBy},${sort.direction}`;
} else {
finalSorting = `${finalSorting}&${sort.orderBy},${sort.direction}`;
}
}
}
return encodeURI(finalSorting);
}
}

View File

@@ -0,0 +1,13 @@
import { TaskListCloudModule } from './task-list-cloud.module';
describe('TaskListCloudModule', () => {
let taskListCloudModule: TaskListCloudModule;
beforeEach(() => {
taskListCloudModule = new TaskListCloudModule();
});
it('should create an instance', () => {
expect(taskListCloudModule).toBeTruthy();
});
});

View File

@@ -0,0 +1,26 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MaterialModule } from '../material.module';
import { TaskListCloudComponent } from './components/task-list-cloud.component';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { TranslateLoaderService, DataTableModule, TemplateModule } from '@alfresco/adf-core';
import { TaskListCloudService } from './services/task-list-cloud.service';
@NgModule({
imports: [
CommonModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderService
}
}),
MaterialModule,
DataTableModule,
TemplateModule
],
declarations: [TaskListCloudComponent],
exports: [TaskListCloudComponent],
providers: [TaskListCloudService]
})
export class TaskListCloudModule { }

View File

@@ -0,0 +1,51 @@
/*!
* @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 { NgModule } from '@angular/core';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { HttpClientModule } from '@angular/common/http';
import { TaskListCloudModule } from '../task-list-cloud.module';
import {
AlfrescoApiService,
AlfrescoApiServiceMock,
AppConfigService,
AppConfigServiceMock,
StorageService,
LogService,
TranslationService,
TranslationMock,
UserPreferencesService,
ContextMenuModule
} from '@alfresco/adf-core';
@NgModule({
imports: [
HttpClientModule,
NoopAnimationsModule,
TaskListCloudModule,
ContextMenuModule
],
providers: [
{ provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock },
{ provide: AppConfigService, useClass: AppConfigServiceMock },
{ provide: TranslationService, useClass: TranslationMock },
StorageService,
LogService,
UserPreferencesService
]
})
export class TaskListTestingModule {}

View File

@@ -18,7 +18,6 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { HttpClientModule } from '@angular/common/http'; import { HttpClientModule } from '@angular/common/http';
import { AppListCloudModule } from '../app-list-cloud.module';
import { import {
AlfrescoApiService, AlfrescoApiService,
AlfrescoApiServiceMock, AlfrescoApiServiceMock,
@@ -27,21 +26,24 @@ import {
StorageService, StorageService,
LogService, LogService,
TranslationService, TranslationService,
TranslationMock TranslationMock,
UserPreferencesService,
ContextMenuModule
} from '@alfresco/adf-core'; } from '@alfresco/adf-core';
@NgModule({ @NgModule({
imports: [ imports: [
HttpClientModule, HttpClientModule,
NoopAnimationsModule, NoopAnimationsModule,
AppListCloudModule ContextMenuModule
], ],
providers: [ providers: [
{ provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock }, { provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock },
{ provide: AppConfigService, useClass: AppConfigServiceMock }, { provide: AppConfigService, useClass: AppConfigServiceMock },
{ provide: TranslationService, useClass: TranslationMock }, { provide: TranslationService, useClass: TranslationMock },
StorageService, StorageService,
LogService LogService,
UserPreferencesService
] ]
}) })
export class AppListTestingModule {} export class ProcessServiceCloudTestingModule {}

View File

@@ -16,4 +16,6 @@
*/ */
export * from './lib/process-services-cloud.module'; export * from './lib/process-services-cloud.module';
export * from './lib/app-list-cloud/app-list-cloud.module'; export * from './lib/app-list-cloud/public-api';
export * from './lib/task-list-cloud/public-api';
export * from './lib/task-cloud/public-api';

View File

@@ -1,41 +0,0 @@
/*!
* @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 'core-js/es7/reflect';
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
declare const pdfjsLib: any;
pdfjsLib.GlobalWorkerOptions.workerSrc = 'node_modules/pdfjs-dist/build/pdf.worker.min.js';
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

View File

@@ -1,5 +1,5 @@
{ {
"extends": "../../tsconfig.json", "extends": "./tsconfig.json",
"compilerOptions": { "compilerOptions": {
"outDir": "../../out-tsc/spec", "outDir": "../../out-tsc/spec",
"types": [ "types": [