Commit 07f15849bbd48d5a2de211eaa5ffc36c9090372a
Committed by
Michel Felipe
1 parent
917544be
Exists in
master
and in
6 other branches
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
| ... | ... | @@ -127,5 +127,17 @@ |
| 127 | 127 | "messages.invalid.passwordMatch": "Your passwords did not match", |
| 128 | 128 | "tasks.menu.all": "All tasks", |
| 129 | 129 | "tasks.menu.all": "See all tasks", |
| 130 | - "tasks.menu.header": "You have {tasks, plural, one{one pending task} other{# pending tasks}}" | |
| 130 | + "tasks.menu.header": "You have {tasks, plural, one{one pending task} other{# pending tasks}}", | |
| 131 | + "tasks.actions.accept": "Accept", | |
| 132 | + "tasks.actions.reject": "Reject", | |
| 133 | + "tasks.header": "Tasks", | |
| 134 | + "tasks.actions.accept.confirmation.title": "Confirm task acceptance?", | |
| 135 | + "tasks.actions.reject.confirmation.title": "Confirm task rejection?", | |
| 136 | + "tasks.actions.confirmation.yes": "Yes", | |
| 137 | + "tasks.actions.confirmation.cancel": "Cancel", | |
| 138 | + "tasks.actions.accept.title": "Good job!", | |
| 139 | + "tasks.actions.reject.title": "Good job!", | |
| 140 | + "tasks.actions.accept.message": "Task Accepted", | |
| 141 | + "tasks.actions.reject.message": "Task Rejected", | |
| 142 | + "tasks.actions.reject.explanation.label": "Rejection explanation" | |
| 131 | 143 | } | ... | ... |
src/languages/pt.json
| ... | ... | @@ -130,5 +130,17 @@ |
| 130 | 130 | "messages.invalid.passwordMatch": "As senhas não coincidem", |
| 131 | 131 | "tasks.menu.all": "Todas as tarefas", |
| 132 | 132 | "tasks.menu.all": "Veja todas as tarefas", |
| 133 | - "tasks.menu.header": "Você tem {tasks, plural, one{uma tarefa pendente} other{# tarefas pendentes}}" | |
| 133 | + "tasks.menu.header": "Você tem {tasks, plural, one{uma tarefa pendente} other{# tarefas pendentes}}", | |
| 134 | + "tasks.actions.accept": "Aceitar", | |
| 135 | + "tasks.actions.reject": "Rejeitar", | |
| 136 | + "tasks.header": "Tarefas", | |
| 137 | + "tasks.actions.accept.confirmation.title": "Confirmar aprovação da tarefa?", | |
| 138 | + "tasks.actions.reject.confirmation.title": "Confirmar reprovação da tarefa?", | |
| 139 | + "tasks.actions.confirmation.yes": "Sim", | |
| 140 | + "tasks.actions.confirmation.cancel": "Cancelar", | |
| 141 | + "tasks.actions.accept.title": "Bom trabalho!", | |
| 142 | + "tasks.actions.reject.title": "Bom trabalho!", | |
| 143 | + "tasks.actions.accept.message": "Tarefa Aceita", | |
| 144 | + "tasks.actions.reject.message": "Tarefa Rejeitada", | |
| 145 | + "tasks.actions.reject.explanation.label": "Motivo da rejeição" | |
| 134 | 146 | } | ... | ... |
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 | } | ... | ... |