Commit ef871ad6d3e9b0127639ca43ca45b05a22e77a2f

Authored by Victor Costa
1 parent 434c23df

Add accept/reject buttons to task list

src/app/task/task-list/accept.html 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +<div class="task-accept task-confirmation">
  2 + <div class="accept-title confirmation-title">{{"tasks.actions.accept.confirmation.title" | translate}}</div>
  3 + <div class="accept-fields confirmation-details">
  4 + <task-accept ng-if="ctrl.currentTask.accept_details" [task]="ctrl.currentTask"></task-accept>
  5 + </div>
  6 + <div class="actions">
  7 + <button type="submit" class="btn btn-default" ng-click="ctrl.callAccept()">{{"tasks.actions.confirmation.yes" | translate}}</button>
  8 + <button type="button" class="btn btn-warning" ng-click="ctrl.cancel()">{{"tasks.actions.confirmation.cancel" | translate}}</button>
  9 + </div>
  10 +</div>
... ...
src/app/task/task-list/reject.html 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +<div class="task-reject task-confirmation">
  2 + <div class="reject-title confirmation-title">{{"tasks.actions.reject.confirmation.title" | translate}}</div>
  3 + <div class="reject-fields confirmation-details">
  4 + <input ng-model="ctrl.rejectionExplanation" id="rejectionExplanationInput" type="text" placeholder="{{'tasks.actions.reject.explanation.label' | translate}}" class="rejection-explanation form-control">
  5 + </div>
  6 + <div class="actions">
  7 + <button type="submit" class="btn btn-default" ng-click="ctrl.callReject()">{{"tasks.actions.confirmation.yes" | translate}}</button>
  8 + <button type="button" class="btn btn-warning" ng-click="ctrl.cancel()">{{"tasks.actions.confirmation.cancel" | translate}}</button>
  9 + </div>
  10 +</div>
... ...
src/app/task/task-list/task-accept.component.ts 0 → 100644
... ... @@ -0,0 +1,21 @@
  1 +import { Input, Inject, Component } from 'ng-forward';
  2 +import { AddMemberTaskAcceptComponent } from "../types/add-member/add-member-task-accept.component";
  3 +
  4 +@Component({
  5 + selector: 'task-accept',
  6 + template: '<div></div>',
  7 + directives: [AddMemberTaskAcceptComponent]
  8 +})
  9 +@Inject("$element", "$scope", "$injector", "$compile")
  10 +export class TaskAcceptComponent {
  11 +
  12 + @Input() task: noosfero.Task;
  13 +
  14 + ngOnInit() {
  15 + let componentName = this.task.type.replace(/::/, '').replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
  16 + componentName += "-task-accept";
  17 + this.$element.replaceWith(this.$compile(`<${componentName} [task]="ctrl.task"></${componentName}>`)(this.$scope));
  18 + }
  19 +
  20 + constructor(private $element: any, private $scope: ng.IScope, private $injector: ng.auto.IInjectorService, private $compile: ng.ICompileService) { }
  21 +}
... ...
src/app/task/task-list/task-list.component.ts
1   -import { Component, Input } from "ng-forward";
  1 +import { Component, Input, Inject } from "ng-forward";
  2 +import { NotificationService } from "../../shared/services/notification.service";
  3 +import { TaskService } from "../../../lib/ng-noosfero-api/http/task.service";
  4 +import { TaskAcceptComponent } from "./task-accept.component";
2 5  
3 6 @Component({
4 7 selector: "task-list",
5 8 templateUrl: "app/task/task-list/task-list.html",
  9 + directives: [TaskAcceptComponent]
6 10 })
  11 +@Inject(NotificationService, "$scope", "$uibModal", TaskService)
7 12 export class TaskListComponent {
8 13  
9 14 @Input() tasks: noosfero.Task[];
10 15  
11   - private taskTemplates = ["AddFriend", "AddMember", "CreateCommunity"];
  16 + private taskTemplates = ["AddFriend", "AddMember", "CreateCommunity", "SuggestArticle", "AbuseComplaint"];
  17 +
  18 + rejectionExplanation: string;
  19 + currentTask: noosfero.Task;
  20 + private modalInstance: any = null;
  21 +
  22 + constructor(private notificationService: NotificationService, private $scope: ng.IScope, private $uibModal: any, private taskService: TaskService) { }
12 23  
13 24 getTaskTemplate(task: noosfero.Task) {
14 25 if (this.taskTemplates.indexOf(task.type) >= 0) {
15   - return 'app/task/types/' + task.type + '.html';
  26 + let templateName = this.getTemplateName(task);
  27 + return `app/task/types/${templateName}/${templateName}.html`;
16 28 } else {
17 29 return 'app/task/types/default.html';
18 30 }
19 31 }
  32 +
  33 + accept(task: noosfero.Task) {
  34 + this.currentTask = task;
  35 + if (task.accept_details) {
  36 + this.modalInstance = this.$uibModal.open({
  37 + templateUrl: "app/task/task-list/accept.html",
  38 + controller: TaskListComponent,
  39 + controllerAs: 'modal',
  40 + bindToController: true,
  41 + scope: this.$scope
  42 + });
  43 + } else {
  44 + this.callAccept();
  45 + }
  46 + }
  47 +
  48 + reject(task: noosfero.Task) {
  49 + this.currentTask = task;
  50 + if (task.reject_details) {
  51 + this.modalInstance = this.$uibModal.open({
  52 + templateUrl: "app/task/task-list/reject.html",
  53 + controller: TaskListComponent,
  54 + controllerAs: 'modal',
  55 + bindToController: true,
  56 + scope: this.$scope
  57 + });
  58 + } else {
  59 + this.callReject();
  60 + }
  61 + }
  62 +
  63 + callAccept() {
  64 + this.taskService.finishTask(this.currentTask).then(() => {
  65 + this.removeTask(this.currentTask);
  66 + this.notificationService.success({ title: "tasks.actions.accept.title", message: "tasks.actions.accept.message" });
  67 + }).finally(() => {
  68 + this.cancel();
  69 + });
  70 + }
  71 +
  72 + callReject() {
  73 + this.taskService.cancelTask(this.currentTask).then(() => {
  74 + this.removeTask(this.currentTask);
  75 + this.notificationService.success({ title: "tasks.actions.reject.title", message: "tasks.actions.reject.message" });
  76 + }).finally(() => {
  77 + this.cancel();
  78 + });
  79 + }
  80 +
  81 + cancel() {
  82 + if (this.modalInstance) {
  83 + this.modalInstance.close();
  84 + this.modalInstance = null;
  85 + }
  86 + if (this.currentTask) {
  87 + this.currentTask = null;
  88 + }
  89 + }
  90 +
  91 + private getTemplateName(task: noosfero.Task) {
  92 + return task.type.replace(/::/, '').replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase());
  93 + }
  94 +
  95 + private removeTask(task: noosfero.Task) {
  96 + let index = this.tasks.indexOf(task, 0);
  97 + if (index > -1) {
  98 + this.tasks.splice(index, 1);
  99 + }
  100 + }
20 101 }
... ...
src/app/task/task-list/task-list.html
... ... @@ -8,6 +8,14 @@
8 8 <div class="task">
9 9 <ng-include src="ctrl.getTaskTemplate(task)"></ng-include>
10 10 </div>
  11 + <div class="actions">
  12 + <a href="#" ng-if="!task.accept_disabled" ng-click="ctrl.accept(task)" class="accept" uib-tooltip="{{'tasks.actions.accept' | translate}}">
  13 + <i class="fa fa-check"></i>
  14 + </a>
  15 + <a href="#" ng-if="!task.reject_disabled" ng-click="ctrl.reject(task)" class="reject" uib-tooltip="{{'tasks.actions.reject' | translate}}">
  16 + <i class="fa fa-close"></i>
  17 + </a>
  18 + </div>
11 19 <span class="time">
12 20 <span class="bullet-separator">•</span> <span am-time-ago="task.created_at | dateFormat"></span>
13 21 </span>
... ...
src/app/task/task-list/task-list.scss
  1 +$task-action-accept-color: #77c123;
  2 +$task-action-reject-color: #d64e18;
  3 +
1 4 .task-list {
2 5 width: 100%;
3 6 padding: 0;
... ... @@ -24,15 +27,36 @@
24 27 }
25 28 .task-body {
26 29 margin-left: 35px;
27   - padding: 3px;
  30 + padding: 2px;
28 31 .task {
29 32 display: inline-block;
  33 + color: #949494;
30 34 .task-icon {
31 35 font-size: 18px;
32 36 color: #e84e40;
33 37 }
34 38 .requestor, .target {
35 39 font-style: italic;
  40 + color: #676767;
  41 + }
  42 + }
  43 + .actions {
  44 + display: inline-block;
  45 + font-size: 19px;
  46 + a {
  47 + text-decoration: none;
  48 + }
  49 + .accept {
  50 + color: $task-action-accept-color;
  51 + &:hover {
  52 + color: darken($task-action-accept-color, 10%);
  53 + }
  54 + }
  55 + .reject {
  56 + color: $task-action-reject-color;
  57 + &:hover {
  58 + color: darken($task-action-reject-color, 10%);
  59 + }
36 60 }
37 61 }
38 62 .time {
... ... @@ -45,3 +69,18 @@
45 69 }
46 70 }
47 71 }
  72 +.task-confirmation {
  73 + @extend .form-group;
  74 + .confirmation-details {
  75 + margin-bottom: 20px;
  76 + }
  77 + .confirmation-title {
  78 + text-align: center;
  79 + font-size: 30px;
  80 + font-weight: bold;
  81 + margin-bottom: 20px;
  82 + }
  83 + .actions {
  84 + text-align: center;
  85 + }
  86 +}
... ...
src/app/task/tasks-menu/tasks-menu.component.ts
... ... @@ -11,14 +11,14 @@ export class TasksMenuComponent {
11 11  
12 12 tasks: noosfero.Task[];
13 13 total: number;
14   - perPage: 5;
  14 + perPage = 5;
15 15 person: noosfero.Person;
16 16  
17 17 constructor(private taskService: TaskService, private session: SessionService) { }
18 18  
19 19 ngOnInit() {
20 20 this.person = this.session.currentUser() ? this.session.currentUser().person : null;
21   - this.taskService.getAllPending({ per_page: this.perPage }).then((result: noosfero.RestResult) => {
  21 + this.taskService.getAllPending({ per_page: this.perPage }).then((result: noosfero.RestResult<noosfero.Task[]>) => {
22 22 this.total = result.headers('total');
23 23 this.tasks = result.data;
24 24 });
... ...
src/app/task/tasks-menu/tasks-menu.scss
... ... @@ -12,14 +12,13 @@ tasks-menu {
12 12 }
13 13 .all-tasks {
14 14 text-align: center;
15   - width: 97%;
  15 + width: 100%;
16 16 display: block;
17   - margin-left: auto;
18   - margin-right: auto;
  17 + line-height: 25px;
19 18 }
20 19 .task-panel {
21 20 width: 550px;
22   - padding-top: 0;
  21 + padding: 0;
23 22 }
24 23 .task-menu-header {
25 24 text-align: center;
... ...
src/app/task/tasks/tasks.component.ts
... ... @@ -14,14 +14,14 @@ export class TasksComponent {
14 14 tasks: noosfero.Task[];
15 15 total: number;
16 16 currentPage: number;
17   - perPage: 5;
  17 + perPage = 5;
18 18  
19 19 constructor(private taskService: TaskService) {
20 20 this.loadPage();
21 21 }
22 22  
23 23 loadPage() {
24   - this.taskService.getAllPending({ page: this.currentPage, per_page: this.perPage }).then((result: noosfero.RestResult) => {
  24 + this.taskService.getAllPending({ page: this.currentPage, per_page: this.perPage }).then((result: noosfero.RestResult<noosfero.Task[]>) => {
25 25 this.total = result.headers('total');
26 26 this.tasks = result.data;
27 27 });
... ...
src/app/task/tasks/tasks.html
1   -<h3>Tarefas</h3>
  1 +<h3>{{"tasks.header" | translate}}</h3>
2 2  
3 3 <task-list [tasks]="vm.tasks"></task-list>
4 4  
... ...
src/app/task/types/AddFriend.html
... ... @@ -1,2 +0,0 @@
1   -<i class="task-icon fa fa-user-plus"></i>
2   -<span class="requestor">{{task.requestor.name}}</span> wants to be friend of <span class="target">{{task.target.name}}</span>
src/app/task/types/AddMember.html
... ... @@ -1,2 +0,0 @@
1   -<i class="task-icon fa fa-user-plus"></i>
2   -<span class="requestor">{{task.requestor.name}}</span> wants to join <span class="target">{{task.target.name}}</span>
src/app/task/types/CreateCommunity.html
... ... @@ -1,2 +0,0 @@
1   -<i class="task-icon fa fa-users"></i>
2   -<span class="requestor">{{task.requestor.name}}</span> wants to create a new community: <span class="target">{{task.data.name}}</span>
src/app/task/types/abuse-complaint/abuse-complaint.html 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +<i class="task-icon fa fa-fw fa-user-times"></i>
  2 +<span class="requestor">{{task.requestor.name}}</span> was reported due to inappropriate behavior
... ...
src/app/task/types/add-friend/add-friend.html 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +<i class="task-icon fa fa-fw fa-user-plus"></i>
  2 +<span class="requestor">{{task.requestor.name}}</span> wants to be friend of <span class="target">{{task.target.name}}</span>
... ...
src/app/task/types/add-member/add-member-accept.html 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +<div class="add-member-details">
  2 + <label>Select Roles:</label>
  3 + <div class="form-group roles">
  4 + <div class="checkbox" ng-repeat="role in ctrl.roles">
  5 + <input type="checkbox"> {{role}}
  6 + </div>
  7 + </div>
  8 +</div>
... ...
src/app/task/types/add-member/add-member-task-accept.component.ts 0 → 100644
... ... @@ -0,0 +1,17 @@
  1 +import { Component, Input, Inject } from "ng-forward";
  2 +
  3 +@Component({
  4 + selector: "add-member-task-accept",
  5 + templateUrl: "app/task/types/add-member/add-member-accept.html",
  6 +})
  7 +export class AddMemberTaskAcceptComponent {
  8 +
  9 + @Input() task: noosfero.Task;
  10 + roles: any;
  11 +
  12 + constructor() {
  13 + //TODO list roles from API
  14 + this.roles = ["Profile Administrator", "Member", "Moderator"];
  15 + }
  16 +
  17 +}
... ...
src/app/task/types/add-member/add-member.html 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +<i class="task-icon fa fa-fw fa-user-plus"></i>
  2 +<span class="requestor">{{task.requestor.name}}</span> wants to join <span class="target">{{task.target.name}}</span>
... ...
src/app/task/types/add-member/add-member.scss 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +.add-member-details {
  2 + .roles {
  3 + margin-left: 40px;
  4 + }
  5 +}
... ...
src/app/task/types/create-community/create-community.html 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +<i class="task-icon fa fa-fw fa-users"></i>
  2 +<span class="requestor">{{task.requestor.name}}</span> wants to create a new community: <span class="target">{{task.data.name}}</span>
... ...
src/app/task/types/suggest-article/suggest-article.html 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +<i class="task-icon fa fa-fw fa-file-text"></i>
  2 +<span class="requestor">{{task.requestor.name}}</span> suggested a new article: <span class="target">{{task.data.article.name}}</span>
... ...
src/languages/en.json
... ... @@ -103,5 +103,17 @@
103 103 "date.on": "On",
104 104 "tasks.menu.all": "All tasks",
105 105 "tasks.menu.all": "See all tasks",
106   - "tasks.menu.header": "You have {tasks, plural, one{one pending task} other{# pending tasks}}"
  106 + "tasks.menu.header": "You have {tasks, plural, one{one pending task} other{# pending tasks}}",
  107 + "tasks.actions.accept": "Accept",
  108 + "tasks.actions.reject": "Reject",
  109 + "tasks.header": "Tasks",
  110 + "tasks.actions.accept.confirmation.title": "Confirm task acceptance?",
  111 + "tasks.actions.reject.confirmation.title": "Confirm task rejection?",
  112 + "tasks.actions.confirmation.yes": "Yes",
  113 + "tasks.actions.confirmation.cancel": "Cancel",
  114 + "tasks.actions.accept.title": "Good job!",
  115 + "tasks.actions.reject.title": "Good job!",
  116 + "tasks.actions.accept.message": "Task Accepted",
  117 + "tasks.actions.reject.message": "Task Rejected",
  118 + "tasks.actions.reject.explanation.label": "Rejection explanation"
107 119 }
... ...
src/languages/pt.json
... ... @@ -103,5 +103,17 @@
103 103 "date.on": "Em",
104 104 "tasks.menu.all": "Todas as tarefas",
105 105 "tasks.menu.all": "Veja todas as tarefas",
106   - "tasks.menu.header": "Você tem {tasks, plural, one{uma tarefa pendente} other{# tarefas pendentes}}"
  106 + "tasks.menu.header": "Você tem {tasks, plural, one{uma tarefa pendente} other{# tarefas pendentes}}",
  107 + "tasks.actions.accept": "Aceitar",
  108 + "tasks.actions.reject": "Rejeitar",
  109 + "tasks.header": "Tarefas",
  110 + "tasks.actions.accept.confirmation.title": "Confirmar aprovação da tarefa?",
  111 + "tasks.actions.reject.confirmation.title": "Confirmar reprovação da tarefa?",
  112 + "tasks.actions.confirmation.yes": "Sim",
  113 + "tasks.actions.confirmation.cancel": "Cancelar",
  114 + "tasks.actions.accept.title": "Bom trabalho!",
  115 + "tasks.actions.reject.title": "Bom trabalho!",
  116 + "tasks.actions.accept.message": "Tarefa Aceita",
  117 + "tasks.actions.reject.message": "Tarefa Rejeitada",
  118 + "tasks.actions.reject.explanation.label": "Motivo da rejeição"
107 119 }
... ...
src/lib/ng-noosfero-api/http/task.service.ts
... ... @@ -24,4 +24,14 @@ export class TaskService extends RestangularService&lt;noosfero.Task&gt; {
24 24 params['all_pending'] = true;
25 25 return this.list(null, params);
26 26 }
  27 +
  28 + finishTask(task: noosfero.Task) {
  29 + let element = this.getElement(task.id);
  30 + return element.customPUT(null, "finish");
  31 + }
  32 +
  33 + cancelTask(task: noosfero.Task) {
  34 + let element = this.getElement(task.id);
  35 + return element.customPUT(null, "cancel");
  36 + }
27 37 }
... ...
src/lib/ng-noosfero-api/interfaces/task.ts
... ... @@ -7,5 +7,7 @@ namespace noosfero {
7 7 */
8 8 export interface Task extends RestModel {
9 9 type: string;
  10 + accept_details: boolean;
  11 + reject_details: boolean;
10 12 }
11 13 }
... ...