Improve the activiti task details

This commit is contained in:
mauriziovitale84 2016-07-27 09:44:09 +01:00
parent 5e9cbcdfeb
commit d612cc8ffa
20 changed files with 673 additions and 27 deletions

View File

@ -5,8 +5,31 @@
}
},
"TASK_DETAILS": {
"LABELS": {
"ASSIGNEE": "Assignee",
"DUE": "Due",
"FORM": "Form",
"PEOPLE": "People",
"COMMENTS": "Comments",
"CHECKLIST": "Checklist"
},
"MESSAGES": {
"NONE": "No task details found."
},
"FORM": {
"NONE": "No form."
},
"DUE": {
"NONE": "No due date."
},
"PEOPLE": {
"NONE": "No people involved."
},
"COMMENTS": {
"NONE": "No comments."
},
"CHECKLIST": {
"NONE": "No checklist."
}
},
"TASK_FILTERS": {

View File

@ -5,8 +5,19 @@
}
},
"TASK_DETAILS": {
"LABELS": {
"ASSIGNEE": "Assegnatario",
"DUE": "Scadenza",
"FORM": "Form"
},
"MESSAGES": {
"NONE": "Nessun dettaglio task trovato."
},
"FORM": {
"NONE": "Nessuna form."
},
"DUE": {
"NONE": "Nessuna data di scadenza."
}
},
"TASK_FILTERS": {

View File

@ -0,0 +1,11 @@
:host {
width: 100%;
}
.activiti-label {
color: rgb(255,152,0);
}
.material-icons:hover {
color: rgb(255, 152, 0);
}

View File

@ -0,0 +1,19 @@
<span class="activiti-label mdl-badge"
[attr.data-badge]="checklist?.length">{{ 'TASK_DETAILS.LABELS.CHECKLIST' | translate }}</span>
<div id="addChecklist" (click)="add()" class="icon material-icons">add</div>
<div class="mdl-tooltip" for="addChecklist">
Add a checklist
</div>
<div class="menu-container" *ngIf="checklist?.length > 0">
<ul class='mdl-list'>
<li class="mdl-list__item" *ngFor="let check of checklist">
<span class="mdl-list__item-primary-content">
<i class="material-icons mdl-list__item-icon">done</i>
{{check.name}}
</span>
</li>
</ul>
</div>
<div *ngIf="checklist?.length === 0">
{{ 'TASK_DETAILS.CHECKLIST.NONE' | translate }}
</div>

View File

@ -0,0 +1,81 @@
/*!
* @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, OnChanges } from '@angular/core';
import { AlfrescoTranslationService, AlfrescoAuthenticationService, AlfrescoPipeTranslate } from 'ng2-alfresco-core';
import { ActivitiTaskListService } from './../services/activiti-tasklist.service';
import { TaskDetailsModel } from '../models/task-details.model';
declare let componentHandler: any;
declare let __moduleName: string;
@Component({
selector: 'activiti-checklist',
moduleId: __moduleName,
templateUrl: './activiti-checklist.component.html',
styleUrls: ['./activiti-checklist.component.css'],
providers: [ActivitiTaskListService],
pipes: [ AlfrescoPipeTranslate ]
})
export class ActivitiChecklist implements OnInit, OnChanges {
@Input()
taskId: string;
checklist: TaskDetailsModel [] = [];
/**
* Constructor
* @param auth
* @param translate
*/
constructor(private auth: AlfrescoAuthenticationService,
private translate: AlfrescoTranslationService,
private activitiTaskList: ActivitiTaskListService) {
if (translate) {
translate.addTranslationFolder('node_modules/ng2-activiti-tasklist');
}
}
ngOnInit() {
if (this.taskId) {
this.load(this.taskId);
}
}
ngOnChanges(change) {
this.load(this.taskId);
}
public add() {
alert('Add CheckList');
}
public load(taskId: string) {
if (this.taskId) {
this.activitiTaskList.getTaskChecklist(this.taskId).subscribe(
(res: TaskDetailsModel[]) => {
this.checklist = res;
}
);
} else {
this.checklist = [];
}
}
}

View File

@ -0,0 +1,11 @@
:host {
width: 100%;
}
.activiti-label {
color: rgb(255,152,0);
}
.material-icons:hover {
color: rgb(255, 152, 0);
}

View File

@ -0,0 +1,35 @@
<span class="activiti-label mdl-badge"
[attr.data-badge]="comments?.length">{{ 'TASK_DETAILS.LABELS.COMMENTS' |translate }}</span>
<div id="addComment" (click)="showDialog()" class="icon material-icons">add</div>
<div class="mdl-tooltip" for="addComment">
Add a comment
</div>
<div class="menu-container" *ngIf="comments?.length > 0">
<ul class='mdl-list'>
<li class="mdl-list__item" *ngFor="let comment of comments">
<span class="mdl-list__item-primary-content">
<i class="material-icons mdl-list__item-icon">comment</i>
{{comment.message}}
</span>
</li>
</ul>
</div>
<div *ngIf="comments?.length === 0">
{{ 'TASK_DETAILS.COMMENTS.NONE' | translate }}
</div>
<dialog class="mdl-dialog" style="width: 35%;" #dialog>
<h4 class="mdl-dialog__title">New comment</h4>
<div class="mdl-dialog__content">
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<textarea class="mdl-textfield__input" type="text" rows="1" id="commentText"></textarea>
<label class="mdl-textfield__label" for="commentText">Text</label>
</div>
</div>
<div class="mdl-dialog__actions">
<button type="button" (click)="add()" class="mdl-button">Add Comment</button>
<button type="button" (click)="cancel()" class="mdl-button close">Cancel</button>
</div>
</dialog>

View File

@ -0,0 +1,104 @@
/*!
* @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, OnChanges, ViewChild } from '@angular/core';
import { AlfrescoTranslationService, AlfrescoAuthenticationService, AlfrescoPipeTranslate } from 'ng2-alfresco-core';
import { ActivitiTaskListService } from './../services/activiti-tasklist.service';
import { Comment } from '../models/comment.model';
declare let componentHandler: any;
declare let __moduleName: string;
@Component({
selector: 'activiti-comments',
moduleId: __moduleName,
templateUrl: './activiti-comments.component.html',
styleUrls: ['./activiti-comments.component.css'],
providers: [ActivitiTaskListService],
pipes: [ AlfrescoPipeTranslate ]
})
export class ActivitiComments implements OnInit, OnChanges {
@Input()
taskId: string;
@ViewChild('dialog')
dialog: any;
comments: Comment [] = [];
/**
* Constructor
* @param auth
* @param translate
*/
constructor(private auth: AlfrescoAuthenticationService,
private translate: AlfrescoTranslationService,
private activitiTaskList: ActivitiTaskListService) {
if (translate) {
translate.addTranslationFolder('node_modules/ng2-activiti-tasklist');
}
}
ngOnInit() {
if (this.taskId) {
this.load(this.taskId);
}
}
ngOnChanges(change) {
this.load(this.taskId);
}
public load(taskId: string) {
if (this.taskId) {
this.activitiTaskList.getTaskComments(this.taskId).subscribe(
(res: Comment[]) => {
this.comments = res;
}
);
} else {
this.comments = [];
}
}
public showDialog() {
if (this.dialog) {
this.dialog.nativeElement.showModal();
}
}
public add() {
alert('add comment');
if (this.taskId) {
this.activitiTaskList.addTaskComment(this.taskId, 'test comment').subscribe(
(res: Comment[]) => {
this.comments = res;
}
);
}
this.cancel();
}
public cancel() {
if (this.dialog) {
this.dialog.nativeElement.close();
}
}
}

View File

@ -0,0 +1,11 @@
:host {
width: 100%;
}
.activiti-label {
color: rgb(255,152,0);
}
.material-icons:hover {
color: rgb(255, 152, 0);
}

View File

@ -0,0 +1,19 @@
<span class="activiti-label mdl-badge"
[attr.data-badge]="people?.length">{{ 'TASK_DETAILS.LABELS.PEOPLE' | translate }}</span>
<div id="addPeople" class="icon material-icons">add</div>
<div class="mdl-tooltip" for="addPeople">
Add a people
</div>
<div class="menu-container" *ngIf="people?.length > 0">
<ul class='mdl-list'>
<li class="mdl-list__item" *ngFor="let user of people">
<span class="mdl-list__item-primary-content">
<i class="material-icons mdl-list__item-icon">face</i>
{{user.firstName}}
</span>
</li>
</ul>
</div>
<div *ngIf="people?.length === 0">
{{ 'TASK_DETAILS.PEOPLE.NONE' | translate }}
</div>

View File

@ -0,0 +1,55 @@
/*!
* @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 } from '@angular/core';
import { AlfrescoTranslationService, AlfrescoAuthenticationService, AlfrescoPipeTranslate } from 'ng2-alfresco-core';
import { User } from '../models/user.model';
declare let componentHandler: any;
declare let __moduleName: string;
@Component({
selector: 'activiti-people',
moduleId: __moduleName,
templateUrl: './activiti-people.component.html',
styleUrls: ['./activiti-people.component.css'],
pipes: [ AlfrescoPipeTranslate ]
})
export class ActivitiPeople implements OnInit {
@Input()
people: User [] = [];
/**
* Constructor
* @param auth
* @param translate
*/
constructor(private auth: AlfrescoAuthenticationService,
private translate: AlfrescoTranslationService) {
if (translate) {
translate.addTranslationFolder('node_modules/ng2-activiti-tasklist');
}
}
ngOnInit() {
}
}

View File

@ -3,12 +3,17 @@
</div>
<div *ngIf="taskDetails">
<h2 class="mdl-card__title-text">{{taskDetails.name}}</h2>
<activiti-task-header [taskDetails]="taskDetails"></activiti-task-header>
<div class="mdl-grid">
<div class="mdl-cell mdl-cell--4-col">Assignee: {{taskDetails.assignee.lastName}}</div>
<div *ngIf="taskDetails.dueDate" class="mdl-cell mdl-cell--4-col">Due: {{taskDetails.dueDate}}</div>
<div *ngIf="!taskDetails.dueDate" class="mdl-cell mdl-cell--4-col">Due: No due date</div>
<div class="mdl-cell mdl-cell--4-col">Form: {{taskForm?.name}}</div>
<div class="mdl-cell mdl-cell--4-col">
<activiti-people [people]="taskPeople"></activiti-people>
</div>
<div class="mdl-cell mdl-cell--4-col">
<activiti-comments [taskId]="taskId"></activiti-comments>
</div>
<div class="mdl-cell mdl-cell--4-col">
<activiti-checklist [taskId]="taskId"></activiti-checklist>
</div>
</div>
<activiti-form *ngIf="taskDetails.formKey" [taskId]="taskDetails.id" #activitiForm ></activiti-form>
</div>

View File

@ -18,7 +18,12 @@
import { Component, Input, OnInit, OnChanges } from '@angular/core';
import { AlfrescoTranslationService, AlfrescoAuthenticationService, AlfrescoPipeTranslate } from 'ng2-alfresco-core';
import { ActivitiTaskListService } from './../services/activiti-tasklist.service';
import { ActivitiTaskHeader } from './activiti-task-header.component';
import { ActivitiComments } from './activiti-comments.component';
import { ActivitiChecklist } from './activiti-checklist.component';
import { ActivitiPeople } from './activiti-people.component';
import { TaskDetailsModel } from '../models/task-details.model';
import { User } from '../models/user.model';
import { ActivitiForm, FormModel, FormService } from 'ng2-activiti-form';
@ -30,8 +35,8 @@ declare let __moduleName: string;
moduleId: __moduleName,
templateUrl: './activiti-task-details.component.html',
styleUrls: ['./activiti-task-details.component.css'],
providers: [ActivitiTaskListService, FormService],
directives: [ActivitiForm],
providers: [ ActivitiTaskListService, FormService ],
directives: [ ActivitiTaskHeader, ActivitiPeople, ActivitiComments, ActivitiChecklist, ActivitiForm ],
pipes: [ AlfrescoPipeTranslate ]
})
@ -44,6 +49,8 @@ export class ActivitiTaskDetails implements OnInit, OnChanges {
taskForm: FormModel;
taskPeople: User[] = [];
/**
* Constructor
* @param auth
@ -76,16 +83,15 @@ export class ActivitiTaskDetails implements OnInit, OnChanges {
loadDetails(id: string) {
this.taskForm = null;
this.taskPeople = [];
if (id) {
this.activitiTaskList.getTaskDetails(id).subscribe(
(res: TaskDetailsModel) => {
this.taskDetails = res;
if (this.taskDetails && this.taskDetails.formKey) {
this.activitiForm.getTaskForm(this.taskDetails.id).subscribe(
(response) => {
this.taskForm = response;
}
);
if (this.taskDetails && this.taskDetails.involvedPeople) {
this.taskDetails.involvedPeople.forEach((user) => {
this.taskPeople.push(new User(user.id, user.email, user.firstName, user.lastName));
});
}
console.log(this.taskDetails);
}

View File

@ -0,0 +1,7 @@
:host {
width: 100%;
}
.activiti-label {
color: rgb(255,152,0);
}

View File

@ -0,0 +1,18 @@
<div *ngIf="taskDetails">
<div class="mdl-grid">
<div class="mdl-cell mdl-cell--4-col">
<span class="activiti-label">{{ 'TASK_DETAILS.LABELS.ASSIGNEE' | translate }}</span>:
{{taskDetails.assignee.lastName }}
</div>
<div class="mdl-cell mdl-cell--4-col">
<span class="activiti-label">
{{ 'TASK_DETAILS.LABELS.DUE' | translate }}</span>:
{{taskDetails?.dueDate ? taskDetails.dueDate : ('TASK_DETAILS.DUE.NONE' |translate) }}
</div>
<div class="mdl-cell mdl-cell--4-col">
<span class="activiti-label">{{ 'TASK_DETAILS.LABELS.FORM' | translate }}</span>:
{{taskForm?.name ? taskForm.name : ('TASK_DETAILS.FORM.NONE' | translate) }}
</div>
</div>
</div>

View File

@ -0,0 +1,82 @@
/*!
* @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, OnChanges } from '@angular/core';
import { AlfrescoTranslationService, AlfrescoAuthenticationService, AlfrescoPipeTranslate } from 'ng2-alfresco-core';
import { TaskDetailsModel } from '../models/task-details.model';
import { FormModel, FormService } from 'ng2-activiti-form';
declare let componentHandler: any;
declare let __moduleName: string;
@Component({
selector: 'activiti-task-header',
moduleId: __moduleName,
templateUrl: './activiti-task-header.component.html',
styleUrls: ['./activiti-task-header.component.css'],
providers: [ FormService ],
pipes: [ AlfrescoPipeTranslate ]
})
export class ActivitiTaskHeader implements OnInit, OnChanges {
@Input()
taskDetails: TaskDetailsModel;
taskForm: FormModel;
/**
* Constructor
* @param auth
* @param translate
*/
constructor(private auth: AlfrescoAuthenticationService,
private activitiForm: FormService,
private translate: AlfrescoTranslationService) {
if (translate) {
translate.addTranslationFolder('node_modules/ng2-activiti-tasklist');
}
}
ngOnInit() {
if (this.taskDetails && this.taskDetails.formKey) {
this.load(this.taskDetails.id);
}
}
ngOnChanges(change) {
if (this.taskDetails && this.taskDetails.formKey) {
this.load(this.taskDetails.id);
} else {
this.taskForm = null;
}
}
public load(taskId: string) {
if (taskId) {
this.activitiForm.getTaskForm(taskId).subscribe(
(response) => {
this.taskForm = response;
},
(err) => {
console.error(err);
}
);
}
}
}

View File

@ -0,0 +1,39 @@
/*!
* @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.
*/
/**
*
* This object represent the comment of a task.
*
*
* @returns {Comment} .
*/
import { User } from './user.model';
export class Comment {
id: number;
message: string;
created: string;
createdBy: User;
constructor(id: number, message: string, created: string, createdBy: User) {
this.id = id;
this.message = message;
this.created = created;
this.createdBy = createdBy;
}
}

View File

@ -22,6 +22,8 @@
*
* @returns {TaskDetailsModel} .
*/
import { User } from './user.model';
export class TaskDetailsModel {
id: string;
name: string;
@ -89,17 +91,3 @@ export class TaskDetailsModel {
this.taskDefinitionKey = obj.taskDefinitionKey;
}
}
export class User {
id: number;
email: string;
firstName: string;
lastName: string;
constructor(id: number, email: string, firstName: string, lastName: string) {
this.id = id;
this.email = email;
this.firstName = firstName;
this.lastName = lastName;
}
}

View File

@ -0,0 +1,38 @@
/*!
* @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.
*/
/**
*
* This object represent the user.
*
*
* @returns {User} .
*/
export class User {
id: number;
email: string;
firstName: string;
lastName: string;
constructor(id: number, email: string, firstName: string, lastName: string) {
this.id = id;
this.email = email;
this.firstName = firstName;
this.lastName = lastName;
}
}

View File

@ -20,6 +20,9 @@ import { AlfrescoSettingsService } from 'ng2-alfresco-core';
import { Http, Headers, RequestOptions, Response } from '@angular/http';
import { Observable } from 'rxjs/Rx';
import { FilterModel } from '../models/filter.model';
import { Comment } from '../models/comment.model';
import { User } from '../models/user.model';
import { TaskDetailsModel } from '../models/task-details.model';
@Injectable()
@ -69,6 +72,49 @@ export class ActivitiTaskListService {
.catch(this.handleError);
}
getTaskComments(id: string): Observable<Comment[]> {
return Observable.fromPromise(this.callApiTaskComments(id))
.map(res => res.json())
.map((response: any) => {
let comments: Comment[] = [];
response.data.forEach((comment) => {
let user = new User(
comment.createdBy.id, comment.createdBy.email, comment.createdBy.firstName, comment.createdBy.lastName);
comments.push(new Comment(comment.id, comment.message, comment.created, user));
});
return comments;
})
.catch(this.handleError);
}
addTaskComment(id: string, message: string): Observable<Comment[]> {
return Observable.fromPromise(this.callApiAddTaskComment(id, message))
.map(res => res.json())
.map((response: any) => {
let comments: Comment[] = [];
response.data.forEach((comment) => {
let user = new User(
comment.createdBy.id, comment.createdBy.email, comment.createdBy.firstName, comment.createdBy.lastName);
comments.push(new Comment(comment.id, comment.message, comment.created, user));
});
return comments;
})
.catch(this.handleError);
}
getTaskChecklist(id: string): Observable<TaskDetailsModel[]> {
return Observable.fromPromise(this.callApiTaskChecklist(id))
.map(res => res.json())
.map((response: any) => {
let checklists: TaskDetailsModel[] = [];
response.data.forEach((checklist) => {
checklists.push(new TaskDetailsModel(checklist));
});
return checklists;
})
.catch(this.handleError);
}
completeTask(id: string): Observable<TaskDetailsModel> {
return Observable.fromPromise(this.callApiCompleteTask(id))
.catch(this.handleError);
@ -114,6 +160,43 @@ export class ActivitiTaskListService {
.get(url, options).toPromise();
}
private callApiTaskComments(id: string) {
let url = `${this.basePath}/api/enterprise/tasks/${id}/comments`;
let headers = new Headers({
'Content-Type': 'application/json',
'Cache-Control': 'no-cache'
});
let options = new RequestOptions({headers: headers});
return this.http
.get(url, options).toPromise();
}
private callApiAddTaskComment(id: string, message: string) {
let url = `${this.basePath}/api/enterprise/tasks/${id}/comments`;
let headers = new Headers({
'Content-Type': 'application/json',
'Cache-Control': 'no-cache'
});
let body = JSON.stringify({message: message});
let options = new RequestOptions({headers: headers});
return this.http
.post(url, body, options).toPromise();
}
private callApiTaskChecklist(id: string) {
let url = `${this.basePath}/api/enterprise/tasks/${id}/checklist`;
let headers = new Headers({
'Content-Type': 'application/json',
'Cache-Control': 'no-cache'
});
let options = new RequestOptions({headers: headers});
return this.http
.get(url, options).toPromise();
}
private callApiCompleteTask(id: string) {
let url = this.alfrescoSettingsService.getBPMApiBaseUrl() + `/api/enterprise/tasks/${id}/action/complete`;
// let url = `http://localhost:9999/activiti-app/app/rest/tasks/${id}/action/complete`;