Commit ef871ad6d3e9b0127639ca43ca45b05a22e77a2f
1 parent
434c23df
Add accept/reject buttons to task list
Showing
25 changed files
with
259 additions
and
21 deletions
Show diff stats
... | ... | @@ -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> | ... | ... |
... | ... | @@ -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> | ... | ... |
... | ... | @@ -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
src/app/task/types/AddFriend.html
src/app/task/types/AddMember.html
src/app/task/types/CreateCommunity.html
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/create-community/create-community.html
0 → 100644
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<noosfero.Task> { |
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 | } | ... | ... |