mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[ADF-3769] Task Header Cloud Component for APS 2 (#4140)
* [ADF-3769] Task Header Cloud Component for APS 2 * [ADF-3796] Add error catch to affected-libs.sh * [ADF-3769] Add readOnly mode and remove selectFirstRow in Task List Cloud component
This commit is contained in:
committed by
Eugenio Romano
parent
28da31c550
commit
f2b5300705
@@ -4,3 +4,9 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<h4>Simple page to show the taskId: {{ taskId }} of the app: {{ applicationName }}</h4>
|
<h4>Simple page to show the taskId: {{ taskId }} of the app: {{ applicationName }}</h4>
|
||||||
|
|
||||||
|
<adf-cloud-task-header
|
||||||
|
[appName]="applicationName"
|
||||||
|
[taskId]="taskId"
|
||||||
|
[readOnly]="readOnly">
|
||||||
|
</adf-cloud-task-header>
|
||||||
|
@@ -26,6 +26,7 @@ export class TaskDetailsCloudDemoComponent {
|
|||||||
|
|
||||||
taskId: string;
|
taskId: string;
|
||||||
applicationName: string;
|
applicationName: string;
|
||||||
|
readOnly = false;
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute, private router: Router) {
|
constructor(private route: ActivatedRoute, private router: Router) {
|
||||||
this.route.params.subscribe((params) => {
|
this.route.params.subscribe((params) => {
|
||||||
|
@@ -74,13 +74,6 @@
|
|||||||
<input matInput class="form-control" [formControl]="taskProcessInstanceId" data-automation-id="process-instance-id">
|
<input matInput class="form-control" [formControl]="taskProcessInstanceId" data-automation-id="process-instance-id">
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-slide-toggle
|
|
||||||
[color]="'primary'"
|
|
||||||
(change)="toggleSelectFirstRow()"
|
|
||||||
[checked]="selectFirstRow"
|
|
||||||
data-automation-id="select-first-row">
|
|
||||||
Select First Row
|
|
||||||
</mat-slide-toggle>
|
|
||||||
<mat-slide-toggle
|
<mat-slide-toggle
|
||||||
[color]="'primary'"
|
[color]="'primary'"
|
||||||
(change)="toggleMultiselect()"
|
(change)="toggleMultiselect()"
|
||||||
@@ -111,7 +104,6 @@
|
|||||||
[dueDate]="dueDate"
|
[dueDate]="dueDate"
|
||||||
[createdDate]="createdDate"
|
[createdDate]="createdDate"
|
||||||
[selectionMode]="selectionMode"
|
[selectionMode]="selectionMode"
|
||||||
[selectFirstRow]="selectFirstRow"
|
|
||||||
[multiselect]="multiSelection"
|
[multiselect]="multiSelection"
|
||||||
(rowsSelected)="showSelectedRows($event)">
|
(rowsSelected)="showSelectedRows($event)">
|
||||||
<data-columns>
|
<data-columns>
|
||||||
|
@@ -43,7 +43,6 @@ export class TaskListCloudDemoComponent implements OnInit {
|
|||||||
createdDate: string;
|
createdDate: string;
|
||||||
dueDate: string;
|
dueDate: string;
|
||||||
selectionMode: string;
|
selectionMode: string;
|
||||||
selectFirstRow: boolean = false;
|
|
||||||
multiSelection: boolean = false;
|
multiSelection: boolean = false;
|
||||||
|
|
||||||
statusOptions = [
|
statusOptions = [
|
||||||
@@ -153,10 +152,6 @@ export class TaskListCloudDemoComponent implements OnInit {
|
|||||||
this.multiSelection = !this.multiSelection;
|
this.multiSelection = !this.multiSelection;
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleSelectFirstRow() {
|
|
||||||
this.selectFirstRow = !this.selectFirstRow;
|
|
||||||
}
|
|
||||||
|
|
||||||
showSelectedRows(rows: any) {
|
showSelectedRows(rows: any) {
|
||||||
|
|
||||||
const selectedRows = rows.map((row) => {
|
const selectedRows = rows.map((row) => {
|
||||||
|
52
docs/process-services-cloud/task-header-cloud.component.md
Normal file
52
docs/process-services-cloud/task-header-cloud.component.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# [Task Header component](../../lib/process-services-cloud/task-header/components/task-header-cloud.component.ts "Defined in task-header.component.ts")
|
||||||
|
|
||||||
|
Shows all the information related to a task.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Basic Usage
|
||||||
|
|
||||||
|
```html
|
||||||
|
<adf-cloud-task-header
|
||||||
|
[appName]="applicationName"
|
||||||
|
[taskId]="taskId">
|
||||||
|
</adf-cloud-task-header>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Class members
|
||||||
|
|
||||||
|
### Properties
|
||||||
|
|
||||||
|
| Name | Type | Default value | Description |
|
||||||
|
| ---- | ---- | ------------- | ----------- |
|
||||||
|
| appName | `string` | | (required) The name of the application. |
|
||||||
|
| taskId | `string` | | (required) The id of the Task. |
|
||||||
|
| readOnly | `boolean` | false | Flag to set the component in Read Only Mode. This mode makes all the cells not clickable and not editable. |
|
||||||
|
|
||||||
|
### Events
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
| ---- | ---- | ----------- |
|
||||||
|
| claim | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<any>` | Emitted when the task is claimed. |
|
||||||
|
| unclaim | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<any>` | Emitted when the task is unclaimed (ie, requeued). |
|
||||||
|
|
||||||
|
## Details
|
||||||
|
|
||||||
|
The component populates an internal array of
|
||||||
|
[CardViewModel](../core/card-view.component.md) with the information that we want to display.
|
||||||
|
|
||||||
|
By default all properties are displayed:
|
||||||
|
|
||||||
|
**_assignee_**, **_status_**, **_priority_**, **_dueDate_**, **_category_**, **_parentName_**, **_created_**, **_id_**, **_description_**, **_formName_**.
|
||||||
|
|
||||||
|
However, you can also choose which properties to show using a configuration in `app.config.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"adf-cloud-task-header": {
|
||||||
|
"presets": {
|
||||||
|
"properties" : [ "assignee", "status", "priority", "parentName"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
With this configuration, only the four listed properties will be shown.
|
@@ -62,7 +62,6 @@ when the task list is empty:
|
|||||||
| parentTaskId | `string` | "" | Filter the tasks. Display only tasks with parentTaskId equal to the supplied value. |
|
| parentTaskId | `string` | "" | Filter the tasks. Display only tasks with parentTaskId equal to the supplied value. |
|
||||||
| processDefinitionId | `string` | "" | Filter the tasks. Display only tasks with processDefinitionId equal to the supplied value. |
|
| processDefinitionId | `string` | "" | Filter the tasks. Display only tasks with processDefinitionId equal to the supplied value. |
|
||||||
| processInstanceId | `string` | "" | Filter the tasks. Display only tasks with processInstanceId equal to the supplied value. |
|
| processInstanceId | `string` | "" | Filter the tasks. Display only tasks with processInstanceId equal to the supplied value. |
|
||||||
| 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 the Cmd (macOS) or Ctrl (Win) modifier key to toggle selection for multiple rows. |
|
| selectionMode | `string` | "single" | Row selection mode. Can be none, `single` or `multiple`. For `multiple` mode, you can use the Cmd (macOS) or Ctrl (Win) modifier key to toggle selection for multiple rows. |
|
||||||
| sorting | [`TaskListCloudSortingModel`](../../lib/process-services-cloud/src/lib/task/task-list/models/task-list-sorting.model.ts)`[]` | | Specifies how the table should be sorted. The parameters are for BE sorting. |
|
| sorting | [`TaskListCloudSortingModel`](../../lib/process-services-cloud/src/lib/task/task-list/models/task-list-sorting.model.ts)`[]` | | Specifies how the table should be sorted. The parameters are for BE sorting. |
|
||||||
| status | `string` | "" | Filter the tasks. Display only tasks with status equal to the supplied value. |
|
| status | `string` | "" | Filter the tasks. Display only tasks with status equal to the supplied value. |
|
||||||
|
@@ -151,5 +151,36 @@
|
|||||||
"ERROR": {
|
"ERROR": {
|
||||||
"NOT_FOUND": "No group found with the name {{groupName}}"
|
"NOT_FOUND": "No group found with the name {{groupName}}"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"ADF_CLOUD_TASK_HEADER": {
|
||||||
|
"BUTTON": {
|
||||||
|
"CLAIM": "Claim",
|
||||||
|
"UNCLAIM": "Requeue"
|
||||||
|
},
|
||||||
|
"PROPERTIES": {
|
||||||
|
"TASK_NAME": "Task",
|
||||||
|
"THUMBNAIL": "Thumbnail",
|
||||||
|
"DURATION": "Duration",
|
||||||
|
"PARENT_TASK_ID": "Parent task id",
|
||||||
|
"NAME": "Name",
|
||||||
|
"ASSIGNEE": "Assignee",
|
||||||
|
"ASSIGNEE_DEFAULT": "No assignee",
|
||||||
|
"PRIORITY": "Priority",
|
||||||
|
"DUE_DATE": "Due Date",
|
||||||
|
"DUE_DATE_DEFAULT": "No date",
|
||||||
|
"STATUS": "Status",
|
||||||
|
"CATEGORY": "Category",
|
||||||
|
"CATEGORY_DEFAULT": "No category",
|
||||||
|
"PARENT_NAME": "Parent name",
|
||||||
|
"PARENT_NAME_DEFAULT": "No parent",
|
||||||
|
"CREATED_BY": "Created By",
|
||||||
|
"CREATED": "Created",
|
||||||
|
"END_DATE": "End date",
|
||||||
|
"ID": "ID",
|
||||||
|
"DESCRIPTION": "Description",
|
||||||
|
"DESCRIPTION_DEFAULT": "No description",
|
||||||
|
"FORM_NAME": "Form Name",
|
||||||
|
"FORM_NAME_DEFAULT": "No form"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,4 +18,6 @@
|
|||||||
export * from './task-list/public-api';
|
export * from './task-list/public-api';
|
||||||
export * from './task-filters/public-api';
|
export * from './task-filters/public-api';
|
||||||
export * from './start-task/public-api';
|
export * from './start-task/public-api';
|
||||||
|
export * from './task-header/public-api';
|
||||||
|
|
||||||
export * from './task-cloud.module';
|
export * from './task-cloud.module';
|
||||||
|
@@ -42,37 +42,43 @@ export class TaskDetailsCloudModel {
|
|||||||
serviceName: string;
|
serviceName: string;
|
||||||
serviceFullName: string;
|
serviceFullName: string;
|
||||||
serviceVersion: string;
|
serviceVersion: string;
|
||||||
|
managerOfCandidateGroup: boolean;
|
||||||
|
memberOfCandidateGroup: boolean;
|
||||||
|
memberOfCandidateUsers: boolean;
|
||||||
|
|
||||||
constructor(obj?: any) {
|
constructor(obj?: any) {
|
||||||
if (obj) {
|
if (obj) {
|
||||||
this.id = obj.id || null;
|
this.id = obj.id || null;
|
||||||
this.name = obj.name || null;
|
this.name = obj.name || null;
|
||||||
this.appName = obj.appName || null;
|
this.appName = obj.appName || null;
|
||||||
this.assignee = obj.assignee || null;
|
this.assignee = obj.assignee || null;
|
||||||
this.appVersion = obj.appVersion || null;
|
this.appVersion = obj.appVersion || null;
|
||||||
this.createdDate = obj.createdDate || null;
|
this.createdDate = obj.createdDate || null;
|
||||||
this.claimedDate = obj.claimedDate || null;
|
this.claimedDate = obj.claimedDate || null;
|
||||||
this.formKey = obj.formKey || null;
|
this.formKey = obj.formKey || null;
|
||||||
this.description = obj.description || null;
|
this.description = obj.description || null;
|
||||||
this.dueDate = obj.dueDate || null;
|
this.dueDate = obj.dueDate || null;
|
||||||
this.lastModified = obj.lastModified || null;
|
this.lastModified = obj.lastModified || null;
|
||||||
this.lastModifiedTo = obj.lastModifiedTo || null;
|
this.lastModifiedTo = obj.lastModifiedTo || null;
|
||||||
this.lastModifiedFrom = obj.lastModifiedFrom || null;
|
this.lastModifiedFrom = obj.lastModifiedFrom || null;
|
||||||
this.owner = obj.owner || null;
|
this.owner = obj.owner || null;
|
||||||
this.parentTaskId = obj.parentTaskId || null;
|
this.parentTaskId = obj.parentTaskId || null;
|
||||||
this.priority = obj.priority || null;
|
this.priority = obj.priority || null;
|
||||||
this.processDefinitionId = obj.processDefinitionId || null;
|
this.processDefinitionId = obj.processDefinitionId || null;
|
||||||
this.processInstanceId = obj.processInstanceId || null;
|
this.processInstanceId = obj.processInstanceId || null;
|
||||||
this.serviceType = obj.serviceType || null;
|
this.serviceType = obj.serviceType || null;
|
||||||
this.status = obj.status || null;
|
this.status = obj.status || null;
|
||||||
this.standAlone = obj.standAlone || null;
|
this.standAlone = obj.standAlone || null;
|
||||||
this.serviceName = obj.serviceName || null;
|
this.serviceName = obj.serviceName || null;
|
||||||
this.serviceName = obj.serviceName || null;
|
this.serviceName = obj.serviceName || null;
|
||||||
this.serviceFullName = obj.serviceFullName || null;
|
this.serviceFullName = obj.serviceFullName || null;
|
||||||
this.serviceVersion = obj.serviceVersion || null;
|
this.serviceVersion = obj.serviceVersion || null;
|
||||||
}
|
this.managerOfCandidateGroup = obj.managerOfCandidateGroup || null;
|
||||||
|
this.memberOfCandidateGroup = obj.memberOfCandidateGroup || null;
|
||||||
|
this.memberOfCandidateUsers = obj.memberOfCandidateUsers || null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface StartTaskCloudResponseModel {
|
export interface StartTaskCloudResponseModel {
|
||||||
entry: TaskDetailsCloudModel;
|
entry: TaskDetailsCloudModel;
|
||||||
|
@@ -19,17 +19,20 @@ import { NgModule } from '@angular/core';
|
|||||||
import { TaskListCloudModule } from './task-list/task-list-cloud.module';
|
import { TaskListCloudModule } from './task-list/task-list-cloud.module';
|
||||||
import { TaskFiltersCloudModule } from './task-filters/task-filters-cloud.module';
|
import { TaskFiltersCloudModule } from './task-filters/task-filters-cloud.module';
|
||||||
import { StartTaskCloudModule } from './start-task/start-task-cloud.module';
|
import { StartTaskCloudModule } from './start-task/start-task-cloud.module';
|
||||||
|
import { TaskHeaderCloudModule } from './task-header/task-header-cloud.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
TaskListCloudModule,
|
TaskListCloudModule,
|
||||||
TaskFiltersCloudModule,
|
TaskFiltersCloudModule,
|
||||||
StartTaskCloudModule
|
StartTaskCloudModule,
|
||||||
|
TaskHeaderCloudModule
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
TaskListCloudModule,
|
TaskListCloudModule,
|
||||||
TaskFiltersCloudModule,
|
TaskFiltersCloudModule,
|
||||||
StartTaskCloudModule
|
StartTaskCloudModule,
|
||||||
|
TaskHeaderCloudModule
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class TaskCloudModule { }
|
export class TaskCloudModule { }
|
||||||
|
@@ -0,0 +1,15 @@
|
|||||||
|
<mat-card *ngIf="isTaskValid()" class="adf-card-container">
|
||||||
|
<mat-card-content>
|
||||||
|
<adf-card-view
|
||||||
|
[properties]="properties"
|
||||||
|
[editable]="!isCompleted()">
|
||||||
|
</adf-card-view>
|
||||||
|
</mat-card-content>
|
||||||
|
|
||||||
|
<mat-card-actions class="adf-controls">
|
||||||
|
<button *ngIf="isTaskClaimedByCandidateMember()" mat-button data-automation-id="header-unclaim-button" id="unclaim-task" (click)="unclaimTask()" class="adf-claim-controls">{{ 'ADF_CLOUD_TASK_HEADER.BUTTON.UNCLAIM' | translate }}
|
||||||
|
</button>
|
||||||
|
<button *ngIf="isTaskClaimable()" mat-button data-automation-id="header-claim-button" id="claim-task" (click)="claimTask()" class="adf-claim-controls">{{ 'ADF_CLOUD_TASK_HEADER.BUTTON.CLAIM' | translate }}
|
||||||
|
</button>
|
||||||
|
</mat-card-actions>
|
||||||
|
</mat-card>
|
@@ -0,0 +1,40 @@
|
|||||||
|
@mixin adf-task-list-header-theme($theme) {
|
||||||
|
$primary: map-get($theme, primary);
|
||||||
|
|
||||||
|
.adf {
|
||||||
|
&-controls {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-edit-controls {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-switch-to-edit-mode,
|
||||||
|
&-save-edit-mode {
|
||||||
|
color: mat-color($primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-cancel-edit-mode,
|
||||||
|
&-claim-controls {
|
||||||
|
color: rgb(131, 131, 131);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-card-container {
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and ($mat-small) {
|
||||||
|
adf-card-view .adf-property-value {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,155 @@
|
|||||||
|
/*!
|
||||||
|
* @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, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { setupTestBed, AppConfigService } from '@alfresco/adf-core';
|
||||||
|
import { TaskHeaderCloudComponent } from './task-header-cloud.component';
|
||||||
|
import { taskDetailsCloudMock } from '../mocks/task-details-cloud.mock';
|
||||||
|
import { TaskHeaderCloudModule } from '../task-header-cloud.module';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { TaskHeaderCloudService } from '../services/task-header-cloud.service';
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
|
||||||
|
|
||||||
|
describe('TaskHeaderComponent', () => {
|
||||||
|
let component: TaskHeaderCloudComponent;
|
||||||
|
let fixture: ComponentFixture<TaskHeaderCloudComponent>;
|
||||||
|
let service: TaskHeaderCloudService;
|
||||||
|
let appConfigService: AppConfigService;
|
||||||
|
|
||||||
|
setupTestBed({
|
||||||
|
imports: [
|
||||||
|
ProcessServiceCloudTestingModule,
|
||||||
|
TaskHeaderCloudModule
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(TaskHeaderCloudComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
component.appName = 'myApp';
|
||||||
|
component.taskId = taskDetailsCloudMock.id;
|
||||||
|
service = TestBed.get(TaskHeaderCloudService);
|
||||||
|
appConfigService = TestBed.get(AppConfigService);
|
||||||
|
spyOn(service, 'getTaskById').and.returnValue(of(taskDetailsCloudMock));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render empty component if no task details provided', async(() => {
|
||||||
|
component.appName = undefined;
|
||||||
|
component.taskId = undefined;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.debugElement.children.length).toBe(0);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should display assignee', async(() => {
|
||||||
|
component.ngOnInit();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
let formNameEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-assignee"] span'));
|
||||||
|
expect(formNameEl.nativeElement.innerText).toBe('Wilbur Adams');
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should display placeholder if no assignee', async(() => {
|
||||||
|
component.ngOnInit();
|
||||||
|
component.taskDetails.assignee = null;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
let valueEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-assignee"] span'));
|
||||||
|
expect(valueEl.nativeElement.innerText).toBe('ADF_CLOUD_TASK_HEADER.PROPERTIES.ASSIGNEE_DEFAULT');
|
||||||
|
});
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should display priority', async(() => {
|
||||||
|
component.ngOnInit();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
let formNameEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-priority"]'));
|
||||||
|
expect(formNameEl.nativeElement.innerText).toBe('5');
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should display due date', async(() => {
|
||||||
|
component.ngOnInit();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
let valueEl = fixture.debugElement.query(By.css('[data-automation-id="header-dueDate"] .adf-property-value'));
|
||||||
|
expect(valueEl.nativeElement.innerText.trim()).toBe('Dec 18 2018');
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should display placeholder if no due date', async(() => {
|
||||||
|
component.ngOnInit();
|
||||||
|
component.taskDetails.dueDate = null;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
let valueEl = fixture.debugElement.query(By.css('[data-automation-id="header-dueDate"] .adf-property-value'));
|
||||||
|
expect(valueEl.nativeElement.innerText.trim()).toBe('ADF_CLOUD_TASK_HEADER.PROPERTIES.DUE_DATE_DEFAULT');
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should display the default parent value if is undefined', async(() => {
|
||||||
|
component.ngOnInit();
|
||||||
|
component.taskDetails.processInstanceId = null;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
let valueEl = fixture.debugElement.query(By.css('[data-automation-id="header-parentName"] .adf-property-value'));
|
||||||
|
expect(valueEl.nativeElement.innerText.trim()).toEqual('ADF_CLOUD_TASK_HEADER.PROPERTIES.PARENT_NAME_DEFAULT');
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('Config Filtering', () => {
|
||||||
|
|
||||||
|
it('should show only the properties from the configuration file', async(() => {
|
||||||
|
spyOn(appConfigService, 'get').and.returnValue(['assignee', 'status']);
|
||||||
|
component.ngOnInit();
|
||||||
|
fixture.detectChanges();
|
||||||
|
let propertyList = fixture.debugElement.queryAll(By.css('.adf-property-list .adf-property'));
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
expect(propertyList).toBeDefined();
|
||||||
|
expect(propertyList).not.toBeNull();
|
||||||
|
expect(propertyList.length).toBe(2);
|
||||||
|
expect(propertyList[0].nativeElement.textContent).toContain('ADF_CLOUD_TASK_HEADER.PROPERTIES.ASSIGNEE');
|
||||||
|
expect(propertyList[1].nativeElement.textContent).toContain('ADF_CLOUD_TASK_HEADER.PROPERTIES.STATUS');
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should show all the default properties if there is no configuration', async(() => {
|
||||||
|
spyOn(appConfigService, 'get').and.returnValue(null);
|
||||||
|
component.ngOnInit();
|
||||||
|
fixture.detectChanges();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
let propertyList = fixture.debugElement.queryAll(By.css('.adf-property-list .adf-property'));
|
||||||
|
expect(propertyList).toBeDefined();
|
||||||
|
expect(propertyList).not.toBeNull();
|
||||||
|
expect(propertyList.length).toBe(component.properties.length);
|
||||||
|
expect(propertyList[0].nativeElement.textContent).toContain('ADF_CLOUD_TASK_HEADER.PROPERTIES.ASSIGNEE');
|
||||||
|
expect(propertyList[1].nativeElement.textContent).toContain('ADF_CLOUD_TASK_HEADER.PROPERTIES.STATUS');
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,273 @@
|
|||||||
|
/*!
|
||||||
|
* @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, Input, OnInit, EventEmitter, Output } from '@angular/core';
|
||||||
|
import {
|
||||||
|
CardViewDateItemModel,
|
||||||
|
CardViewItem,
|
||||||
|
CardViewTextItemModel,
|
||||||
|
CardViewBaseItemModel,
|
||||||
|
TranslationService,
|
||||||
|
AppConfigService,
|
||||||
|
CardViewMapItemModel,
|
||||||
|
UpdateNotification,
|
||||||
|
CardViewUpdateService,
|
||||||
|
StorageService
|
||||||
|
} from '@alfresco/adf-core';
|
||||||
|
import { TaskHeaderCloudService } from '../services/task-header-cloud.service';
|
||||||
|
import { TaskDetailsCloudModel } from '../../start-task/models/task-details-cloud.model';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'adf-cloud-task-header',
|
||||||
|
templateUrl: './task-header-cloud.component.html',
|
||||||
|
styleUrls: ['./task-header-cloud.component.scss']
|
||||||
|
})
|
||||||
|
export class TaskHeaderCloudComponent implements OnInit {
|
||||||
|
|
||||||
|
/** (Required) The appName */
|
||||||
|
@Input()
|
||||||
|
appName: string;
|
||||||
|
|
||||||
|
/** (Required) The id of the task. */
|
||||||
|
@Input()
|
||||||
|
taskId: string;
|
||||||
|
|
||||||
|
/** The id of the task. */
|
||||||
|
@Input()
|
||||||
|
readOnly: boolean = false;
|
||||||
|
|
||||||
|
/** Emitted when the task is claimed. */
|
||||||
|
@Output()
|
||||||
|
claim: EventEmitter<any> = new EventEmitter<any>();
|
||||||
|
|
||||||
|
/** Emitted when the task is unclaimed (ie, requeued). */
|
||||||
|
@Output()
|
||||||
|
unclaim: EventEmitter<any> = new EventEmitter<any>();
|
||||||
|
|
||||||
|
taskDetails: TaskDetailsCloudModel = new TaskDetailsCloudModel();
|
||||||
|
properties: CardViewItem [];
|
||||||
|
inEdit: boolean = false;
|
||||||
|
private currentUser: string;
|
||||||
|
|
||||||
|
constructor(private taskHeaderCloudService: TaskHeaderCloudService,
|
||||||
|
private translationService: TranslationService,
|
||||||
|
private appConfig: AppConfigService,
|
||||||
|
private cardViewUpdateService: CardViewUpdateService,
|
||||||
|
private storage: StorageService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.loadCurrentBpmUserId();
|
||||||
|
if (this.appName && this.taskId) {
|
||||||
|
this.loadTaskDetailsById(this.appName, this.taskId);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cardViewUpdateService.itemUpdated$.subscribe(this.updateTaskDetails.bind(this));
|
||||||
|
}
|
||||||
|
loadCurrentBpmUserId(): any {
|
||||||
|
this.currentUser = this.storage.getItem('USERNAME');
|
||||||
|
}
|
||||||
|
|
||||||
|
loadTaskDetailsById(appName: string, taskId: string): any {
|
||||||
|
this.taskHeaderCloudService.getTaskById(appName, taskId).subscribe(
|
||||||
|
(taskDetails) => {
|
||||||
|
this.taskDetails = taskDetails;
|
||||||
|
this.refreshData();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private initDefaultProperties(parentInfoMap) {
|
||||||
|
return [
|
||||||
|
new CardViewTextItemModel(
|
||||||
|
{
|
||||||
|
label: 'ADF_CLOUD_TASK_HEADER.PROPERTIES.ASSIGNEE',
|
||||||
|
value: this.taskDetails.assignee,
|
||||||
|
key: 'assignee',
|
||||||
|
default: this.translationService.instant('ADF_CLOUD_TASK_HEADER.PROPERTIES.ASSIGNEE_DEFAULT'),
|
||||||
|
icon: 'create'
|
||||||
|
}
|
||||||
|
),
|
||||||
|
new CardViewTextItemModel(
|
||||||
|
{
|
||||||
|
label: 'ADF_CLOUD_TASK_HEADER.PROPERTIES.STATUS',
|
||||||
|
value: this.taskDetails.status,
|
||||||
|
key: 'status'
|
||||||
|
}
|
||||||
|
),
|
||||||
|
new CardViewTextItemModel(
|
||||||
|
{
|
||||||
|
label: 'ADF_CLOUD_TASK_HEADER.PROPERTIES.PRIORITY',
|
||||||
|
value: this.taskDetails.priority,
|
||||||
|
key: 'priority',
|
||||||
|
editable: this.isReadOnlyMode()
|
||||||
|
}
|
||||||
|
),
|
||||||
|
new CardViewDateItemModel(
|
||||||
|
{
|
||||||
|
label: 'ADF_CLOUD_TASK_HEADER.PROPERTIES.DUE_DATE',
|
||||||
|
value: this.taskDetails.dueDate,
|
||||||
|
key: 'dueDate',
|
||||||
|
default: this.translationService.instant('ADF_CLOUD_TASK_HEADER.PROPERTIES.DUE_DATE_DEFAULT'),
|
||||||
|
editable: this.isReadOnlyMode()
|
||||||
|
}
|
||||||
|
),
|
||||||
|
new CardViewTextItemModel(
|
||||||
|
{
|
||||||
|
label: 'ADF_CLOUD_TASK_HEADER.PROPERTIES.CATEGORY',
|
||||||
|
value: this.taskDetails.category,
|
||||||
|
key: 'category',
|
||||||
|
default: this.translationService.instant('ADF_CLOUD_TASK_HEADER.PROPERTIES.CATEGORY_DEFAULT')
|
||||||
|
}
|
||||||
|
),
|
||||||
|
new CardViewDateItemModel(
|
||||||
|
{
|
||||||
|
label: 'ADF_CLOUD_TASK_HEADER.PROPERTIES.CREATED',
|
||||||
|
value: this.taskDetails.createdDate,
|
||||||
|
key: 'created'
|
||||||
|
}
|
||||||
|
),
|
||||||
|
new CardViewMapItemModel(
|
||||||
|
{
|
||||||
|
label: 'ADF_CLOUD_TASK_HEADER.PROPERTIES.PARENT_NAME',
|
||||||
|
value: parentInfoMap,
|
||||||
|
key: 'parentName',
|
||||||
|
default: this.translationService.instant('ADF_CLOUD_TASK_HEADER.PROPERTIES.PARENT_NAME_DEFAULT'),
|
||||||
|
clickable: this.isReadOnlyMode()
|
||||||
|
}
|
||||||
|
),
|
||||||
|
new CardViewTextItemModel(
|
||||||
|
{
|
||||||
|
label: 'ADF_CLOUD_TASK_HEADER.PROPERTIES.PARENT_TASK_ID',
|
||||||
|
value: this.taskDetails.parentTaskId,
|
||||||
|
key: 'parentTaskId'
|
||||||
|
}
|
||||||
|
),
|
||||||
|
new CardViewDateItemModel(
|
||||||
|
{
|
||||||
|
label: 'ADF_CLOUD_TASK_HEADER.PROPERTIES.END_DATE',
|
||||||
|
value: this.taskDetails.claimedDate,
|
||||||
|
key: 'endDate'
|
||||||
|
}
|
||||||
|
),
|
||||||
|
new CardViewTextItemModel(
|
||||||
|
{
|
||||||
|
label: 'ADF_CLOUD_TASK_HEADER.PROPERTIES.ID',
|
||||||
|
value: this.taskDetails.id,
|
||||||
|
key: 'id'
|
||||||
|
}
|
||||||
|
),
|
||||||
|
new CardViewTextItemModel(
|
||||||
|
{
|
||||||
|
label: 'ADF_CLOUD_TASK_HEADER.PROPERTIES.DESCRIPTION',
|
||||||
|
value: this.taskDetails.description,
|
||||||
|
key: 'description',
|
||||||
|
default: this.translationService.instant('ADF_CLOUD_TASK_HEADER.PROPERTIES.DESCRIPTION_DEFAULT'),
|
||||||
|
multiline: true,
|
||||||
|
editable: this.isReadOnlyMode()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh the card data
|
||||||
|
*/
|
||||||
|
refreshData() {
|
||||||
|
if (this.taskDetails) {
|
||||||
|
const defaultProperties = this.initDefaultProperties(this.getParentInfo());
|
||||||
|
const filteredProperties: string[] = this.appConfig.get('adf-cloud-task-header.presets.properties');
|
||||||
|
this.properties = defaultProperties.filter((cardItem) => this.isValidSelection(filteredProperties, cardItem));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save a task detail and update it after a successful response
|
||||||
|
*
|
||||||
|
* @param updateNotification
|
||||||
|
*/
|
||||||
|
private updateTaskDetails(updateNotification: UpdateNotification) {
|
||||||
|
this.taskHeaderCloudService.updateTask(this.appName, this.taskId, updateNotification.changed)
|
||||||
|
.subscribe(
|
||||||
|
(taskDetails) => {
|
||||||
|
this.taskDetails = taskDetails;
|
||||||
|
this.refreshData();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getParentInfo() {
|
||||||
|
if (this.taskDetails.processInstanceId && this.taskDetails.processDefinitionId) {
|
||||||
|
return new Map([[this.taskDetails.processInstanceId, this.taskDetails.processDefinitionId]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isCompleted() {
|
||||||
|
return this.taskDetails && this.taskDetails.status === 'completed';
|
||||||
|
}
|
||||||
|
|
||||||
|
isTaskClaimable(): boolean {
|
||||||
|
return !this.hasAssignee() && this.isCandidateMember();
|
||||||
|
}
|
||||||
|
|
||||||
|
hasAssignee(): boolean {
|
||||||
|
return !!this.taskDetails.assignee ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
isCandidateMember() {
|
||||||
|
return this.taskDetails.managerOfCandidateGroup || this.taskDetails.memberOfCandidateGroup || this.taskDetails.memberOfCandidateUsers;
|
||||||
|
}
|
||||||
|
|
||||||
|
isTaskClaimedByCandidateMember(): boolean {
|
||||||
|
return this.isCandidateMember() && this.isAssignedToCurrentUser() && !this.isCompleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
isAssignedToCurrentUser(): boolean {
|
||||||
|
return this.hasAssignee() && this.isAssignedTo(this.currentUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
isAssignedTo(userName): boolean {
|
||||||
|
return this.hasAssignee() ? this.taskDetails.assignee === userName : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
isTaskValid() {
|
||||||
|
return this.appName && this.taskId;
|
||||||
|
}
|
||||||
|
|
||||||
|
isReadOnlyMode() {
|
||||||
|
return !this.readOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
claimTask() {
|
||||||
|
this.taskHeaderCloudService.claimTask(this.appName, this.taskId, this.currentUser).subscribe(
|
||||||
|
(res: any) => {
|
||||||
|
this.loadTaskDetailsById(this.appName, this.taskId);
|
||||||
|
this.claim.emit(this.taskId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
unclaimTask() {
|
||||||
|
this.taskHeaderCloudService.unclaimTask(this.appName, this.taskId).subscribe(
|
||||||
|
() => {
|
||||||
|
this.loadTaskDetailsById(this.appName, this.taskId);
|
||||||
|
this.unclaim.emit(this.taskId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private isValidSelection(filteredProperties: string[], cardItem: CardViewBaseItemModel): boolean {
|
||||||
|
return filteredProperties ? filteredProperties.indexOf(cardItem.key) >= 0 : true;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,46 @@
|
|||||||
|
/*!
|
||||||
|
* @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 const fakeTaskDetailsCloud = {
|
||||||
|
'entry': {
|
||||||
|
'serviceName': 'task-app-rb',
|
||||||
|
'serviceFullName': 'task-app-rb',
|
||||||
|
'serviceVersion': '',
|
||||||
|
'appName': 'task-app',
|
||||||
|
'appVersion': '',
|
||||||
|
'serviceType': null,
|
||||||
|
'id': '68d54a8f',
|
||||||
|
'assignee': 'Phil Woods',
|
||||||
|
'name': 'This is a new task ',
|
||||||
|
'description': 'This is the description ',
|
||||||
|
'createdDate': 1545048055900,
|
||||||
|
'dueDate': 1545091200000,
|
||||||
|
'claimedDate': 1545140162601,
|
||||||
|
'priority': 0,
|
||||||
|
'category': null,
|
||||||
|
'processDefinitionId': null,
|
||||||
|
'processInstanceId': null,
|
||||||
|
'status': 'ASSIGNED',
|
||||||
|
'owner': 'Phil Woods',
|
||||||
|
'parentTaskId': null,
|
||||||
|
'formKey': null,
|
||||||
|
'lastModified': 1545140162601,
|
||||||
|
'lastModifiedTo': null,
|
||||||
|
'lastModifiedFrom': null,
|
||||||
|
'standAlone': true
|
||||||
|
}
|
||||||
|
};
|
@@ -0,0 +1,48 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { TaskDetailsCloudModel } from '../../start-task/models/task-details-cloud.model';
|
||||||
|
|
||||||
|
export const taskDetailsCloudMock = new TaskDetailsCloudModel(
|
||||||
|
{
|
||||||
|
'serviceName': 'task-app-rb',
|
||||||
|
'serviceFullName': 'task-app-rb',
|
||||||
|
'serviceVersion': '',
|
||||||
|
'appName': 'task-app',
|
||||||
|
'appVersion': '',
|
||||||
|
'serviceType': null,
|
||||||
|
'id': '68d54a8f-01f3-11e9-8e36-0a58646002ad',
|
||||||
|
'assignee': 'Wilbur Adams',
|
||||||
|
'name': 'This is a new task ',
|
||||||
|
'description': 'This is the description ',
|
||||||
|
'createdDate': 1545048055900,
|
||||||
|
'dueDate': 1545091200000,
|
||||||
|
'claimedDate': null,
|
||||||
|
'priority': 5,
|
||||||
|
'category': null,
|
||||||
|
'processDefinitionId': null,
|
||||||
|
'processInstanceId': null,
|
||||||
|
'status': 'ASSIGNED',
|
||||||
|
'owner': 'superadminuser',
|
||||||
|
'parentTaskId': null,
|
||||||
|
'formKey': null,
|
||||||
|
'lastModified': 1545048055900,
|
||||||
|
'lastModifiedTo': null,
|
||||||
|
'lastModifiedFrom': null,
|
||||||
|
'standAlone': true
|
||||||
|
}
|
||||||
|
);
|
@@ -0,0 +1,20 @@
|
|||||||
|
/*!
|
||||||
|
* @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-header-cloud.component';
|
||||||
|
|
||||||
|
export * from './task-header-cloud.module';
|
@@ -0,0 +1,245 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { AlfrescoApiServiceMock, LogService, AppConfigService, StorageService, CoreModule } from '@alfresco/adf-core';
|
||||||
|
import { fakeTaskDetailsCloud } from '../mocks/fake-task-details-response.mock';
|
||||||
|
import { TaskHeaderCloudService } from './task-header-cloud.service';
|
||||||
|
|
||||||
|
describe('Task Header Cloud Service', () => {
|
||||||
|
|
||||||
|
let service: TaskHeaderCloudService;
|
||||||
|
let alfrescoApiMock: AlfrescoApiServiceMock;
|
||||||
|
|
||||||
|
function returnFakeTaskDetailsResults() {
|
||||||
|
return {
|
||||||
|
oauth2Auth: {
|
||||||
|
callCustomApi : () => {
|
||||||
|
return Promise.resolve(fakeTaskDetailsCloud);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
setupTestBed({
|
||||||
|
imports: [
|
||||||
|
CoreModule.forRoot()
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
alfrescoApiMock = new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService() );
|
||||||
|
service = new TaskHeaderCloudService(alfrescoApiMock,
|
||||||
|
new AppConfigService(null),
|
||||||
|
new LogService(new AppConfigService(null)));
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should return the task details when querying by id', (done) => {
|
||||||
|
const appName = 'taskp-app';
|
||||||
|
const taskId = '68d54a8f';
|
||||||
|
spyOn(alfrescoApiMock, 'getInstance').and.callFake(returnFakeTaskDetailsResults);
|
||||||
|
service.getTaskById(appName, taskId).subscribe((res: any) => {
|
||||||
|
expect(res).toBeDefined();
|
||||||
|
expect(res).not.toBeNull();
|
||||||
|
expect(res.appName).toBe('task-app');
|
||||||
|
expect(res.id).toBe('68d54a8f');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error if appName is not defined when querying by id', (done) => {
|
||||||
|
const appName = null;
|
||||||
|
const taskId = '68d54a8f';
|
||||||
|
spyOn(alfrescoApiMock, 'getInstance').and.callFake(returnFakeTaskDetailsResults);
|
||||||
|
service.getTaskById(appName, taskId).subscribe(
|
||||||
|
() => { },
|
||||||
|
(error) => {
|
||||||
|
expect(error).toBe('AppName/TaskId not configured');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error if taskId is not defined when querying by id', (done) => {
|
||||||
|
const appName = 'task-app';
|
||||||
|
const taskId = null;
|
||||||
|
spyOn(alfrescoApiMock, 'getInstance').and.callFake(returnFakeTaskDetailsResults);
|
||||||
|
service.getTaskById(appName, taskId).subscribe(
|
||||||
|
() => { },
|
||||||
|
(error) => {
|
||||||
|
expect(error).toBe('AppName/TaskId not configured');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the task details when updating a task', (done) => {
|
||||||
|
const appName = 'taskp-app';
|
||||||
|
const taskId = '68d54a8f';
|
||||||
|
const updatePayload = { description: 'New description' };
|
||||||
|
spyOn(alfrescoApiMock, 'getInstance').and.callFake(returnFakeTaskDetailsResults);
|
||||||
|
service.updateTask(appName, taskId, updatePayload).subscribe((res: any) => {
|
||||||
|
expect(res).toBeDefined();
|
||||||
|
expect(res).not.toBeNull();
|
||||||
|
expect(res.appName).toBe('task-app');
|
||||||
|
expect(res.id).toBe('68d54a8f');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error if appName is not defined when updating a task', (done) => {
|
||||||
|
const appName = null;
|
||||||
|
const taskId = '68d54a8f';
|
||||||
|
const updatePayload = { description: 'New description' };
|
||||||
|
spyOn(alfrescoApiMock, 'getInstance').and.callFake(returnFakeTaskDetailsResults);
|
||||||
|
service.updateTask(appName, taskId, updatePayload).subscribe(
|
||||||
|
() => { },
|
||||||
|
(error) => {
|
||||||
|
expect(error).toBe('AppName/TaskId not configured');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error if taskId is not defined when updating a task', (done) => {
|
||||||
|
const appName = 'task-app';
|
||||||
|
const taskId = null;
|
||||||
|
const updatePayload = { description: 'New description' };
|
||||||
|
spyOn(alfrescoApiMock, 'getInstance').and.callFake(returnFakeTaskDetailsResults);
|
||||||
|
service.updateTask(appName, taskId, updatePayload).subscribe(
|
||||||
|
() => { },
|
||||||
|
(error) => {
|
||||||
|
expect(error).toBe('AppName/TaskId not configured');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the task details when updating a task', (done) => {
|
||||||
|
const appName = 'taskp-app';
|
||||||
|
const taskId = '68d54a8f';
|
||||||
|
const updatePayload = { description: 'New description' };
|
||||||
|
spyOn(alfrescoApiMock, 'getInstance').and.callFake(returnFakeTaskDetailsResults);
|
||||||
|
service.updateTask(appName, taskId, updatePayload).subscribe((res: any) => {
|
||||||
|
expect(res).toBeDefined();
|
||||||
|
expect(res).not.toBeNull();
|
||||||
|
expect(res.appName).toBe('task-app');
|
||||||
|
expect(res.id).toBe('68d54a8f');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error if appName is not defined when querying by id', (done) => {
|
||||||
|
const appName = null;
|
||||||
|
const taskId = '68d54a8f';
|
||||||
|
const updatePayload = { description: 'New description' };
|
||||||
|
spyOn(alfrescoApiMock, 'getInstance').and.callFake(returnFakeTaskDetailsResults);
|
||||||
|
service.updateTask(appName, taskId, updatePayload).subscribe(
|
||||||
|
() => { },
|
||||||
|
(error) => {
|
||||||
|
expect(error).toBe('AppName/TaskId not configured');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error if taskId is not defined updating a task', (done) => {
|
||||||
|
const appName = 'task-app';
|
||||||
|
const taskId = null;
|
||||||
|
const updatePayload = { description: 'New description' };
|
||||||
|
spyOn(alfrescoApiMock, 'getInstance').and.callFake(returnFakeTaskDetailsResults);
|
||||||
|
service.updateTask(appName, taskId, updatePayload).subscribe(
|
||||||
|
() => { },
|
||||||
|
(error) => {
|
||||||
|
expect(error).toBe('AppName/TaskId not configured');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the task details when claiming a task', (done) => {
|
||||||
|
const appName = 'taskp-app';
|
||||||
|
const assignee = 'user12';
|
||||||
|
const taskId = '68d54a8f';
|
||||||
|
spyOn(alfrescoApiMock, 'getInstance').and.callFake(returnFakeTaskDetailsResults);
|
||||||
|
service.claimTask(appName, taskId, assignee).subscribe((res: any) => {
|
||||||
|
expect(res).toBeDefined();
|
||||||
|
expect(res).not.toBeNull();
|
||||||
|
expect(res.appName).toBe('task-app');
|
||||||
|
expect(res.id).toBe('68d54a8f');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error if appName is not defined when claiming a task', (done) => {
|
||||||
|
const appName = null;
|
||||||
|
const taskId = '68d54a8f';
|
||||||
|
const assignee = 'user12';
|
||||||
|
spyOn(alfrescoApiMock, 'getInstance').and.callFake(returnFakeTaskDetailsResults);
|
||||||
|
service.claimTask(appName, taskId, assignee).subscribe(
|
||||||
|
() => { },
|
||||||
|
(error) => {
|
||||||
|
expect(error).toBe('AppName/TaskId not configured');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error if taskId is not defined when claiming a task', (done) => {
|
||||||
|
const appName = 'task-app';
|
||||||
|
const taskId = null;
|
||||||
|
const assignee = 'user12';
|
||||||
|
spyOn(alfrescoApiMock, 'getInstance').and.callFake(returnFakeTaskDetailsResults);
|
||||||
|
service.claimTask(appName, taskId, assignee).subscribe(
|
||||||
|
() => { },
|
||||||
|
(error) => {
|
||||||
|
expect(error).toBe('AppName/TaskId not configured');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the task details when unclaiming a task', (done) => {
|
||||||
|
const appName = 'taskp-app';
|
||||||
|
const taskId = '68d54a8f';
|
||||||
|
spyOn(alfrescoApiMock, 'getInstance').and.callFake(returnFakeTaskDetailsResults);
|
||||||
|
service.unclaimTask(appName, taskId).subscribe((res: any) => {
|
||||||
|
expect(res).toBeDefined();
|
||||||
|
expect(res).not.toBeNull();
|
||||||
|
expect(res.appName).toBe('task-app');
|
||||||
|
expect(res.id).toBe('68d54a8f');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error if appName is not defined when unclaiming a task', (done) => {
|
||||||
|
const appName = null;
|
||||||
|
const taskId = '68d54a8f';
|
||||||
|
spyOn(alfrescoApiMock, 'getInstance').and.callFake(returnFakeTaskDetailsResults);
|
||||||
|
service.unclaimTask(appName, taskId).subscribe(
|
||||||
|
() => { },
|
||||||
|
(error) => {
|
||||||
|
expect(error).toBe('AppName/TaskId not configured');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error if taskId is not defined when unclaiming a task', (done) => {
|
||||||
|
const appName = 'task-app';
|
||||||
|
const taskId = null;
|
||||||
|
spyOn(alfrescoApiMock, 'getInstance').and.callFake(returnFakeTaskDetailsResults);
|
||||||
|
service.unclaimTask(appName, taskId).subscribe(
|
||||||
|
() => { },
|
||||||
|
(error) => {
|
||||||
|
expect(error).toBe('AppName/TaskId not configured');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@@ -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 { AlfrescoApiService, LogService, AppConfigService } from '@alfresco/adf-core';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Observable, from, throwError } from 'rxjs';
|
||||||
|
import { catchError, map } from 'rxjs/operators';
|
||||||
|
import { TaskDetailsCloudModel } from '../../start-task/models/task-details-cloud.model';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class TaskHeaderCloudService {
|
||||||
|
contextRoot: string;
|
||||||
|
contentTypes = ['application/json'];
|
||||||
|
accepts = ['application/json'];
|
||||||
|
returnType = Object;
|
||||||
|
|
||||||
|
constructor(private alfrescoApiService: AlfrescoApiService,
|
||||||
|
private appConfigService: AppConfigService,
|
||||||
|
private logService: LogService) {
|
||||||
|
this.contextRoot = this.appConfigService.get('bpmHost', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
getTaskById(appName: string, taskId: string): Observable<TaskDetailsCloudModel> {
|
||||||
|
if (appName && taskId) {
|
||||||
|
|
||||||
|
let queryUrl = `${this.contextRoot}/${appName}-query/v1/tasks/${taskId}`;
|
||||||
|
return from(this.alfrescoApiService.getInstance()
|
||||||
|
.oauth2Auth.callCustomApi(queryUrl, 'GET',
|
||||||
|
null, null, null,
|
||||||
|
null, null,
|
||||||
|
this.contentTypes, this.accepts,
|
||||||
|
this.returnType, null, null)
|
||||||
|
).pipe(
|
||||||
|
map((res: any) => {
|
||||||
|
return new TaskDetailsCloudModel(res.entry);
|
||||||
|
}),
|
||||||
|
catchError((err) => this.handleError(err))
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.logService.error('AppName and TaskId are mandatory for querying a task');
|
||||||
|
return throwError('AppName/TaskId not configured');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTask(appName: string, taskId: string, updatePayload: any): any {
|
||||||
|
if (appName && taskId) {
|
||||||
|
|
||||||
|
updatePayload.payloadType = 'UpdateTaskPayload';
|
||||||
|
|
||||||
|
let queryUrl = `${this.contextRoot}/${appName}-rb/v1/tasks/${taskId}`;
|
||||||
|
return from(this.alfrescoApiService.getInstance()
|
||||||
|
.oauth2Auth.callCustomApi(queryUrl, 'PUT',
|
||||||
|
null, null, null,
|
||||||
|
null, updatePayload,
|
||||||
|
this.contentTypes, this.accepts,
|
||||||
|
this.returnType, null, null)
|
||||||
|
).pipe(
|
||||||
|
map((res: any) => {
|
||||||
|
return new TaskDetailsCloudModel(res.entry);
|
||||||
|
}),
|
||||||
|
catchError((err) => this.handleError(err))
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.logService.error('AppName and TaskId are mandatory for querying a task');
|
||||||
|
return throwError('AppName/TaskId not configured');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
claimTask(appName: string, taskId: string, assignee: string): any {
|
||||||
|
if (appName && taskId) {
|
||||||
|
|
||||||
|
let queryUrl = `${this.contextRoot}/${appName}-rb/v1/tasks/${taskId}/claim?assignee=${assignee}`;
|
||||||
|
return from(this.alfrescoApiService.getInstance()
|
||||||
|
.oauth2Auth.callCustomApi(queryUrl, 'POST',
|
||||||
|
null, null, null,
|
||||||
|
null, null,
|
||||||
|
this.contentTypes, this.accepts,
|
||||||
|
this.returnType, null, null)
|
||||||
|
).pipe(
|
||||||
|
map((res: any) => {
|
||||||
|
return new TaskDetailsCloudModel(res.entry);
|
||||||
|
}),
|
||||||
|
catchError((err) => this.handleError(err))
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.logService.error('AppName and TaskId are mandatory for querying a task');
|
||||||
|
return throwError('AppName/TaskId not configured');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unclaimTask(appName: string, taskId: string): any {
|
||||||
|
if (appName && taskId) {
|
||||||
|
|
||||||
|
let queryUrl = `${this.contextRoot}/${appName}-rb/v1/tasks/${taskId}/release`;
|
||||||
|
return from(this.alfrescoApiService.getInstance()
|
||||||
|
.oauth2Auth.callCustomApi(queryUrl, 'POST',
|
||||||
|
null, null, null,
|
||||||
|
null, null,
|
||||||
|
this.contentTypes, this.accepts,
|
||||||
|
this.returnType, null, null)
|
||||||
|
).pipe(
|
||||||
|
map((res: any) => {
|
||||||
|
return new TaskDetailsCloudModel(res.entry);
|
||||||
|
}),
|
||||||
|
catchError((err) => this.handleError(err))
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.logService.error('AppName and TaskId are mandatory for querying a task');
|
||||||
|
return throwError('AppName/TaskId not configured');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleError(error: any) {
|
||||||
|
this.logService.error(error);
|
||||||
|
return throwError(error || 'Server error');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,30 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { TaskHeaderCloudModule } from './task-header-cloud.module';
|
||||||
|
|
||||||
|
describe('TaskCloudModule', () => {
|
||||||
|
let taskHeaderCloudModule: TaskHeaderCloudModule;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
taskHeaderCloudModule = new TaskHeaderCloudModule();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create an instance', () => {
|
||||||
|
expect(taskHeaderCloudModule).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,50 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { CommonModule } from '@angular/common';
|
||||||
|
import { MaterialModule } from '../../material.module';
|
||||||
|
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
|
||||||
|
import { TranslateLoaderService, DataTableModule, TemplateModule, CardViewModule } from '@alfresco/adf-core';
|
||||||
|
import { TaskHeaderCloudComponent } from './components/task-header-cloud.component';
|
||||||
|
import { TaskHeaderCloudService } from './services/task-header-cloud.service';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
TranslateModule.forRoot({
|
||||||
|
loader: {
|
||||||
|
provide: TranslateLoader,
|
||||||
|
useClass: TranslateLoaderService
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
MaterialModule,
|
||||||
|
DataTableModule,
|
||||||
|
TemplateModule,
|
||||||
|
CardViewModule
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
TaskHeaderCloudComponent
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
TaskHeaderCloudComponent
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
TaskHeaderCloudService
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class TaskHeaderCloudModule { }
|
@@ -85,10 +85,6 @@ export class TaskListCloudComponent extends DataTableSchema implements OnChanges
|
|||||||
@Input()
|
@Input()
|
||||||
status: string = '';
|
status: string = '';
|
||||||
|
|
||||||
/** Toggles default selection of the first row. */
|
|
||||||
@Input()
|
|
||||||
selectFirstRow: boolean = true;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define which task id should be selected after reloading. If the task id doesn't
|
* 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.
|
* exist or nothing is passed then the first task will be selected.
|
||||||
@@ -220,9 +216,6 @@ export class TaskListCloudComponent extends DataTableSchema implements OnChanges
|
|||||||
return currentRow.entry.id === taskIdSelected;
|
return currentRow.entry.id === taskIdSelected;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (!dataRow && this.selectFirstRow) {
|
|
||||||
dataRow = this.rows[0];
|
|
||||||
}
|
|
||||||
if (dataRow) {
|
if (dataRow) {
|
||||||
dataRow.isSelected = true;
|
dataRow.isSelected = true;
|
||||||
this.currentInstanceId = dataRow.entry.id;
|
this.currentInstanceId = dataRow.entry.id;
|
||||||
|
@@ -40,7 +40,7 @@ if [ ! -d "$DIRECTORY" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -f $DIRECTORY/deps.txt ]; then
|
if [ ! -f $DIRECTORY/deps.txt ]; then
|
||||||
npm run affected:libs -- $HEAD_SHA_BRANCH "HEAD" > $DIRECTORY/deps.txt
|
npm run affected:libs -- $HEAD_SHA_BRANCH "HEAD" > $DIRECTORY/deps.txt || ( echo "This PR needs to be rebased"; exit 1 )
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cat $DIRECTORY/deps.txt
|
cat $DIRECTORY/deps.txt
|
||||||
|
Reference in New Issue
Block a user