Commit 6c43d39c7ccec7028709ff36197cd564c8dc0fb0

Authored by Leonardo Merlin
2 parents 7a87daca 8a19bd73

Merge branch 'feature-proposal-vote'

src/app/components/article-service/article.service.js
@@ -162,20 +162,15 @@ @@ -162,20 +162,15 @@
162 } 162 }
163 } 163 }
164 164
165 - function voteProposal (proposal_id, params, cbSuccess, cbError){ 165 + function voteProposal (proposal_id, params){
166 var url = service.apiArticles + proposal_id + '/vote'; 166 var url = service.apiArticles + proposal_id + '/vote';
167 var paramsExtended = angular.extend({ 167 var paramsExtended = angular.extend({
168 - private_token: $rootScope.currentUser.private_token  
169 - // private_token: 'e2198fdbcc20409f082829b4b5c0848e' 168 + private_token: $rootScope.temporaryToken
170 }, params); 169 }, params);
171 170
172 var encodedParams = angular.element.param(paramsExtended); 171 var encodedParams = angular.element.param(paramsExtended);
173 172
174 - UtilService.post(url, encodedParams).then(function(response){  
175 - cbSuccess(response);  
176 - }).catch(function(error){  
177 - cbError(error);  
178 - }); 173 + return UtilService.post(url, encodedParams);
179 } 174 }
180 175
181 function getEvents (community_id, params) { 176 function getEvents (community_id, params) {
src/app/components/auth/auth.service.js
@@ -53,11 +53,7 @@ @@ -53,11 +53,7 @@
53 } 53 }
54 54
55 function activate (code) { 55 function activate (code) {
56 - var url = PATH.host +'/api/v1/activate';  
57 - // var data = {  
58 - // private_token: API.token,  
59 - // activation_code: code  
60 - // }; 56 + var url = PATH.host + '/api/v1/activate';
61 var encodedData = 'private_token=' + API.token; 57 var encodedData = 'private_token=' + API.token;
62 encodedData += '&activation_code=' + code; 58 encodedData += '&activation_code=' + code;
63 59
@@ -77,12 +73,7 @@ @@ -77,12 +73,7 @@
77 } 73 }
78 74
79 function changePassword (code, newPassword, newPasswordConfirmation){ 75 function changePassword (code, newPassword, newPasswordConfirmation){
80 - var url = PATH.host +'/api/v1/new_password';  
81 - // var data = {  
82 - // code: code,  
83 - // password: newPassword,  
84 - // password_confirmation: newPasswordConfirmation  
85 - // }; 76 + var url = PATH.host + '/api/v1/new_password';
86 var encodedData = 'code=' + code; 77 var encodedData = 'code=' + code;
87 encodedData += '&password=' + newPassword; 78 encodedData += '&password=' + newPassword;
88 encodedData += '&password_confirmation=' + newPasswordConfirmation; 79 encodedData += '&password_confirmation=' + newPasswordConfirmation;
@@ -103,7 +94,7 @@ @@ -103,7 +94,7 @@
103 } 94 }
104 95
105 function forgotPassword (data){ 96 function forgotPassword (data){
106 - var url = PATH.host +'/api/v1/forgot_password'; 97 + var url = PATH.host + '/api/v1/forgot_password';
107 var encodedData = ([ 98 var encodedData = ([
108 'value=' + data.login, 99 'value=' + data.login,
109 'captcha_text=' + data.captcha_text, 100 'captcha_text=' + data.captcha_text,
@@ -145,9 +136,28 @@ @@ -145,9 +136,28 @@
145 }, function(response) { 136 }, function(response) {
146 $log.debug('AuthService.login [FAIL] response', response); 137 $log.debug('AuthService.login [FAIL] response', response);
147 $rootScope.$broadcast(AUTH_EVENTS.loginFailed); 138 $rootScope.$broadcast(AUTH_EVENTS.loginFailed);
  139 +
  140 + return $q.reject(response);
148 }); 141 });
149 } 142 }
150 143
  144 + function loginCaptcha (data) {
  145 + var url = PATH.host + '/api/v1/login-captcha';
  146 + var encodedData = angular.element.param(data);
  147 +
  148 + return $http.post(url, encodedData).then(function(response){
  149 + // SUCCESS
  150 + $log.debug('AuthService.loginCaptcha [SUCCESS] response', response);
  151 +
  152 + var temporaryToken = response.data.private_token;
  153 + Session.setTemporaryToken(temporaryToken);
  154 + $rootScope.temporaryToken = temporaryToken;
  155 + return temporaryToken;
  156 + }, function(response){
  157 + return $q.reject(response.data);
  158 + });
  159 + }
  160 +
151 function logout () { 161 function logout () {
152 162
153 Session.destroy(); 163 Session.destroy();
@@ -167,28 +177,13 @@ @@ -167,28 +177,13 @@
167 return (service.isAuthenticated() && authorizedRoles.indexOf(Session.userRole) !== -1); 177 return (service.isAuthenticated() && authorizedRoles.indexOf(Session.userRole) !== -1);
168 } 178 }
169 179
170 - // function _encodeObj(obj){  
171 - // var result = [];  
172 - // var str = null;  
173 - // var p = null;  
174 -  
175 - // for (p in obj) {  
176 - // if (obj.hasOwnProperty(p)) {  
177 - // // str = encodeURIComponent(p) + '=' + obj[p];  
178 - // str = p + '=' + obj[p];  
179 - // result.push(str);  
180 - // }  
181 - // }  
182 -  
183 - // return result.join('&');  
184 - // }  
185 -  
186 var service = { 180 var service = {
187 register: register, 181 register: register,
188 activate: activate, 182 activate: activate,
189 changePassword: changePassword, 183 changePassword: changePassword,
190 forgotPassword: forgotPassword, 184 forgotPassword: forgotPassword,
191 login: login, 185 login: login,
  186 + loginCaptcha: loginCaptcha,
192 logout: logout, 187 logout: logout,
193 isAuthenticated: isAuthenticated, 188 isAuthenticated: isAuthenticated,
194 isAuthorized: isAuthorized 189 isAuthorized: isAuthorized
@@ -207,7 +202,7 @@ @@ -207,7 +202,7 @@
207 202
208 service.create = function(data) { 203 service.create = function(data) {
209 204
210 - $localStorage.currentUser = data; 205 + $localStorage.currentUser = data.user;
211 $log.debug('User session created.', $localStorage.currentUser); 206 $log.debug('User session created.', $localStorage.currentUser);
212 207
213 return $localStorage.currentUser; 208 return $localStorage.currentUser;
@@ -224,6 +219,14 @@ @@ -224,6 +219,14 @@
224 return $localStorage.currentUser; 219 return $localStorage.currentUser;
225 }; 220 };
226 221
  222 + service.setTemporaryToken = function (private_token) {
  223 + $localStorage.temporaryToken = private_token;
  224 + };
  225 +
  226 + service.getTemporaryToken = function () {
  227 + return $localStorage.temporaryToken;
  228 + };
  229 +
227 return service; 230 return service;
228 } 231 }
229 232
src/app/components/proposal-box/proposal-box.directive.js
@@ -9,30 +9,34 @@ @@ -9,30 +9,34 @@
9 function proposalBox() { 9 function proposalBox() {
10 10
11 /** @ngInject */ 11 /** @ngInject */
12 - function ProposalBoxController($scope, $rootScope, $state, VOTE_STATUS, VOTE_OPTIONS, $log) { 12 + function ProposalBoxController($scope, $rootScope, $state, $timeout, $interval, $window, VOTE_STATUS, VOTE_OPTIONS, AuthService, DialogaService, $log) {
13 $log.debug('ProposalBoxController'); 13 $log.debug('ProposalBoxController');
14 14
15 var vm = this; 15 var vm = this;
16 vm.$scope = $scope; 16 vm.$scope = $scope;
17 vm.$rootScope = $rootScope; 17 vm.$rootScope = $rootScope;
18 vm.$state = $state; 18 vm.$state = $state;
  19 + vm.$timeout = $timeout;
  20 + vm.$interval = $interval;
  21 + vm.$window = $window;
19 vm.VOTE_STATUS = VOTE_STATUS; 22 vm.VOTE_STATUS = VOTE_STATUS;
20 vm.VOTE_OPTIONS = VOTE_OPTIONS; 23 vm.VOTE_OPTIONS = VOTE_OPTIONS;
  24 + vm.AuthService = AuthService;
21 vm.$log = $log; 25 vm.$log = $log;
22 26
23 vm.init(); 27 vm.init();
24 vm.addListeners(); 28 vm.addListeners();
25 } 29 }
26 30
27 - ProposalBoxController.prototype.init = function () { 31 + ProposalBoxController.prototype.init = function() {
28 32
29 var vm = this; 33 var vm = this;
30 34
31 - vm.canVote = vm.canVote || false; 35 + vm.showVote = vm.showVote || false;
32 vm.focus = vm.focus || false; 36 vm.focus = vm.focus || false;
33 vm.STATE = null; 37 vm.STATE = null;
34 vm.errorOnSkip = false; 38 vm.errorOnSkip = false;
35 - vm.showAuthMessage = null; 39 + vm.showCaptchaForm = null;
36 vm.voteProposalRedirectURI = null; 40 vm.voteProposalRedirectURI = null;
37 41
38 var slug = vm.topic.slug; 42 var slug = vm.topic.slug;
@@ -40,37 +44,52 @@ @@ -40,37 +44,52 @@
40 vm.voteProposalRedirectURI = 'state=programa&task=vote-proposal&slug=' + slug + '&proposal_id=' + proposal_id; 44 vm.voteProposalRedirectURI = 'state=programa&task=vote-proposal&slug=' + slug + '&proposal_id=' + proposal_id;
41 }; 45 };
42 46
43 - ProposalBoxController.prototype.addListeners = function () { 47 + ProposalBoxController.prototype.addListeners = function() {
44 var vm = this; 48 var vm = this;
45 49
46 - vm.$scope.$on('proposal-box:proposal-loaded', function(event, data){  
47 - if(data.success){ 50 + vm.$scope.$on('proposal-box:proposal-loaded', function(event, data) {
  51 + if (data.success) {
48 vm.STATE = null; 52 vm.STATE = null;
49 } 53 }
50 54
51 - if(data.error){ 55 + if (data.error) {
52 vm.errorOnSkip = data.error; 56 vm.errorOnSkip = data.error;
53 } 57 }
54 }); 58 });
55 59
56 - vm.$scope.$on('proposal-box:vote-response', function(event, data){  
57 - vm.$log.debug('proposal-box:vote-response');  
58 - vm.$log.debug('event', event);  
59 - vm.$log.debug('data', data);  
60 -  
61 - if(data.success) { 60 + vm.$scope.$on('proposal-box:vote-response', function(event, data) {
  61 + if (data.success) {
62 vm.STATE = vm.VOTE_STATUS.SUCCESS; 62 vm.STATE = vm.VOTE_STATUS.SUCCESS;
63 } 63 }
64 64
65 - if(data.error) { 65 + if (data.error) {
66 vm.STATE = vm.VOTE_STATUS.ERROR; 66 vm.STATE = vm.VOTE_STATUS.ERROR;
67 } 67 }
68 68
69 - vm.message = data.message; 69 + if (data.code === 401) {
  70 + vm.message = 'Não autorizado.';
  71 + }
  72 +
  73 + vm.messageCode = data.code;
70 }); 74 });
  75 +
  76 + // Load captcha
  77 + var stop = null;
  78 + stop = vm.$interval(function() {
  79 + var $el = angular.element('#serpro_captcha');
  80 +
  81 + if ($el && $el.length > 0) {
  82 + vm.$window.initCaptcha($el[0]);
  83 + vm.$interval.cancel(stop);
  84 + stop = undefined;
  85 + }else {
  86 + vm.$log.debug('captcha element not found.');
  87 + }
  88 +
  89 + }, 10);
71 }; 90 };
72 91
73 - ProposalBoxController.prototype.showContent = function (slug) { 92 + ProposalBoxController.prototype.showContent = function(slug) {
74 var vm = this; 93 var vm = this;
75 94
76 vm.$state.go('programa', { 95 vm.$state.go('programa', {
@@ -81,45 +100,106 @@ @@ -81,45 +100,106 @@
81 }); 100 });
82 }; 101 };
83 102
84 - ProposalBoxController.prototype.vote = function (value) { 103 + ProposalBoxController.prototype.canVote = function() {
85 var vm = this; 104 var vm = this;
86 105
87 - if(vm.$rootScope.currentUser){  
88 - vm.$scope.$emit('proposal-box:vote', {  
89 - OPTION: value,  
90 - proposal_id: vm.proposal.id  
91 - });  
92 - vm.$log.debug('Sending vote', value);  
93 - }else{  
94 - vm.$log.info('Must be logged in...');  
95 - vm.showAuthMessage = true;  
96 - } 106 + return !!vm.$rootScope.temporaryToken;
97 }; 107 };
98 108
99 - ProposalBoxController.prototype.voteDown = function () { 109 + ProposalBoxController.prototype.submitCaptcha = function($event, captchaForm) {
100 var vm = this; 110 var vm = this;
101 111
102 - vm.STATE = vm.VOTE_STATUS.LOADING;  
103 - vm.$scope.$emit('proposal-box:vote', {  
104 - OPTION: vm.VOTE_OPTIONS.DOWN,  
105 - proposal_id: vm.proposal.id 112 + var target = $event.target;
  113 + var $target = angular.element(target);
  114 + var $captcha = $target.find('[name="txtToken_captcha_serpro_gov_br"]');
  115 +
  116 + vm.sendingCaptcha = true;
  117 + vm.AuthService.loginCaptcha({
  118 + captcha_text: captchaForm.captcha_text.$modelValue,
  119 + txtToken_captcha_serpro_gov_br: $captcha.val()
  120 + }).then(function(data) {
  121 + // SUCCESS
  122 + vm.$log.debug('register success.data', data);
  123 +
  124 + // SEND VOTE
  125 + if (vm._oldVoteValue) {
  126 + vm.vote(vm._oldVoteValue);
  127 + vm._oldVoteValue = null;
  128 + }
  129 + // hide captcha form
  130 + vm.showCaptchaForm = false;
  131 +
  132 + }, function(data) {
  133 + // ERROR
  134 + vm.$log.debug('register error.data', data);
  135 +
  136 + vm.sendingCaptchaError = {
  137 + code: data.status,
  138 + message: data.message || ('Erro (' + data.status + '). Já estamos trabalhando para resolver o problema.<br/>Por favor, tente novamente mais tarde')
  139 + };
  140 +
  141 + if (angular.equals(vm.sendingCaptchaError.message, 'Internal captcha validation error')) {
  142 + vm.sendingCaptchaError.message = 'Erro interno ao tentar validar captcha.<br/><br/>Já estamos trabalhando para resolver o problema.<br/>Por favor, tente novamente mais tarde.';
  143 + }
  144 +
  145 + }, function(data) {
  146 + // UPDATE
  147 + vm.$log.debug('register update.data', data);
  148 + }).finally(function() {
  149 + vm.sendingCaptcha = false;
106 }); 150 });
107 - vm.$log.debug('Sending vote');  
108 }; 151 };
109 152
110 - ProposalBoxController.prototype.skip = function () { 153 + ProposalBoxController.prototype.captchaTryAgain = function() {
  154 + var vm = this;
  155 +
  156 + vm.showCaptchaForm = true;
  157 + vm.sendingCaptcha = false;
  158 + vm.sendingCaptchaError = false;
  159 + vm.message = null;
  160 +
  161 + // reload new captcha
  162 + var $el = angular.element('#serpro_captcha');
  163 + vm.$window.reloadCaptcha($el[0]);
  164 +
  165 + // focus on input
  166 + angular.element('#captcha_text').val('').focus();
  167 + };
  168 +
  169 + ProposalBoxController.prototype.vote = function(value) {
  170 + var vm = this;
  171 +
  172 + vm._oldVoteValue = value;
  173 + if (vm.canVote()) {
  174 + if (vm.doVote) {
  175 + vm.doVote({
  176 + proposal_id: vm.proposal.id,
  177 + value: value
  178 + });
  179 + }else {
  180 + vm.$log.error('No vote function to handler votes');
  181 + }
  182 + }else {
  183 + vm.$log.debug('You cannot vote.');
  184 + vm.showCaptchaForm = true;
  185 +
  186 + angular.element('#captcha_text').focus();
  187 + }
  188 + };
  189 +
  190 + ProposalBoxController.prototype.skip = function() {
111 var vm = this; 191 var vm = this;
112 192
113 vm.errorOnSkip = false; 193 vm.errorOnSkip = false;
114 vm.STATE = vm.VOTE_STATUS.LOADING; 194 vm.STATE = vm.VOTE_STATUS.LOADING;
115 - vm.$scope.$emit('proposal-box:vote', {  
116 - OPTION: vm.VOTE_OPTIONS.SKIP,  
117 - proposal_id: vm.proposal.id 195 + vm.doVote({
  196 + proposal_id: vm.proposal.id,
  197 + value: vm.VOTE_OPTIONS.SKIP
118 }); 198 });
119 vm.$log.debug('Sending vote'); 199 vm.$log.debug('Sending vote');
120 }; 200 };
121 201
122 - ProposalBoxController.prototype.getSocialUrl = function () { 202 + ProposalBoxController.prototype.getSocialUrl = function() {
123 var vm = this; 203 var vm = this;
124 204
125 return vm.$state.href('programa', { 205 return vm.$state.href('programa', {
@@ -135,8 +215,9 @@ @@ -135,8 +215,9 @@
135 proposal: '=', 215 proposal: '=',
136 topic: '=', 216 topic: '=',
137 category: '=', 217 category: '=',
138 - canVote: '=',  
139 - focus: '@' 218 + showVote: '=',
  219 + focus: '@',
  220 + doVote: '&'
140 // @ -> Text binding / one-way binding 221 // @ -> Text binding / one-way binding
141 // = -> Direct model binding / two-way binding 222 // = -> Direct model binding / two-way binding
142 // & -> Behaviour binding / Method binding 223 // & -> Behaviour binding / Method binding
src/app/components/proposal-box/proposal-box.html
@@ -52,8 +52,10 @@ @@ -52,8 +52,10 @@
52 </div> 52 </div>
53 <div class="feedback" ng-if="vm.STATE === vm.VOTE_STATUS.ERROR"> 53 <div class="feedback" ng-if="vm.STATE === vm.VOTE_STATUS.ERROR">
54 <p class="feedback--title">Erro!</p> 54 <p class="feedback--title">Erro!</p>
55 - <p class="feedback--message" ng-if="vm.message">  
56 - Motivo: {{vm.message}} 55 + <p class="feedback--message" ng-if="vm.messageCode === 401">
  56 + Não autorizado. Insira um novo captcha.
  57 + <br>
  58 + <button type="button" class="btn btn-link" ng-click="vm.captchaTryAgain()">Gerar novo captcha</button>
57 </p> 59 </p>
58 </div> 60 </div>
59 </div> 61 </div>
@@ -65,18 +67,48 @@ @@ -65,18 +67,48 @@
65 </div> 67 </div>
66 </div> 68 </div>
67 </div> 69 </div>
68 - <div ng-show="vm.showAuthMessage"> 70 + <div ng-show="vm.showCaptchaForm">
69 <div class="proposal-message-panel"> 71 <div class="proposal-message-panel">
70 <div class="row"> 72 <div class="row">
71 <div class="row-height"> 73 <div class="row-height">
72 <div class="col-sm-12 col-height col-middle"> 74 <div class="col-sm-12 col-height col-middle">
73 <div class="inside inside-full-height"> 75 <div class="inside inside-full-height">
74 <div class="content text-center"> 76 <div class="content text-center">
75 - <p>Você precisa estar logado para votar na proposta</p>  
76 - <br>  
77 - <p>  
78 - <a ui-sref="entrar({redirect_uri: vm.voteProposalRedirectURI})">Clique aqui para ir para a página de login</a>  
79 - </p> 77 + <div ng-show="vm.sendingCaptcha">
  78 + <p>Enviando captcha...</p>
  79 + </div>
  80 + <div ng-hide="vm.sendingCaptcha">
  81 + <div class="row feedback-message" ng-show="vm.sendingCaptchaError">
  82 + <div class="col-sm-12">
  83 + <div class="feedback--title alert alert-danger">Erro!</div>
  84 + <div class="feedback--message" ng-if="!vm.message">
  85 + <p ng-bind-html="vm.sendingCaptchaError.message"></p>
  86 + </div>
  87 + <div>
  88 + <button type="reset" class="btn btn-link" ng-click="vm.captchaTryAgain()">Tentar novamente</button>
  89 + <button type="reset" class="btn btn-link" ng-click="vm.showCaptchaForm = false">Voltar</button>
  90 + </div>
  91 + </div>
  92 + </div>
  93 + <div ng-hide="vm.sendingCaptchaError">
  94 + <form name="captchaForm" ng-submit="vm.submitCaptcha($event, captchaForm)">
  95 + <div class="form-group">
  96 + <div id="serpro_captcha" class="captcha"></div>
  97 + <div class="captcha">Digite os caracteres acima:</div>
  98 + <div class="captcha">
  99 + <input type="text" name="captcha_text" id="captcha_text" aria-label="Escreva os caracteres do captcha aqui" ng-model="vm._captcha_text" ng-minlength="" ng-maxlength="" required>
  100 + <validation-messages field="captchaForm.captcha_text"></validation-messages>
  101 + </div>
  102 + </div>
  103 + <div class="form-group">
  104 + <button type="submit" class="btn btn-lg btn-block btn-submit">Enviar</button>
  105 + </div>
  106 + <div class="form-group">
  107 + <button type="reset" class="btn btn-link" ng-click="vm.showCaptchaForm = false">Voltar</button>
  108 + </div>
  109 + </form>
  110 + </div>
  111 + </div>
80 </div> 112 </div>
81 </div> 113 </div>
82 </div> 114 </div>
@@ -112,12 +144,12 @@ @@ -112,12 +144,12 @@
112 <div class="proposal-box--content"> 144 <div class="proposal-box--content">
113 <div class="proposal-box--content-inner">{{vm.proposal.abstract}}</div> 145 <div class="proposal-box--content-inner">{{vm.proposal.abstract}}</div>
114 </div> 146 </div>
115 - <div ng-hide="vm.canVote" class="proposal-box--join"> 147 + <div ng-hide="vm.showVote" class="proposal-box--join">
116 <button class="btn btn-link color-theme-common-fg" ng-click="vm.showContent(vm.topic.slug)"> 148 <button class="btn btn-link color-theme-common-fg" ng-click="vm.showContent(vm.topic.slug)">
117 Participe 149 Participe
118 </button> 150 </button>
119 </div> 151 </div>
120 - <div ng-show="vm.canVote" class="proposal-box--actions text-center"> 152 + <div ng-show="vm.showVote" class="proposal-box--actions text-center">
121 <div class="row"> 153 <div class="row">
122 <div class="col-xs-4"> 154 <div class="col-xs-4">
123 <div class="action vote_for" ng-click="vm.vote(vm.VOTE_OPTIONS.UP)"> 155 <div class="action vote_for" ng-click="vm.vote(vm.VOTE_OPTIONS.UP)">
src/app/components/proposal-box/proposal-box.scss
@@ -91,9 +91,15 @@ @@ -91,9 +91,15 @@
91 .content { 91 .content {
92 color: #262626; 92 color: #262626;
93 font-size: 24px; 93 font-size: 24px;
  94 + font-size: 2.4rem;
94 font-weight: bold; 95 font-weight: bold;
95 line-height: 24px; 96 line-height: 24px;
96 padding: 10px 30px; 97 padding: 10px 30px;
  98 +
  99 + form {
  100 + font-size: 18px;
  101 + font-size: 1.8rem;
  102 + }
97 } 103 }
98 104
99 .message-icon { 105 .message-icon {
@@ -108,11 +114,13 @@ @@ -108,11 +114,13 @@
108 114
109 &--title { 115 &--title {
110 font-size: 22px; 116 font-size: 22px;
  117 + font-size: 2.2rem;
111 font-weight: bold; 118 font-weight: bold;
112 } 119 }
113 120
114 &--message { 121 &--message {
115 font-size: 14px; 122 font-size: 14px;
  123 + font-size: 1.4rem;
116 font-weight: normal; 124 font-weight: normal;
117 line-height: 20px; 125 line-height: 20px;
118 margin-top: 48px; 126 margin-top: 48px;
src/app/components/proposal-grid/proposal-grid.html
1 <div class="proposal-grid row"> 1 <div class="proposal-grid row">
2 <div ng-repeat="proposal in vm.proposals as results"> 2 <div ng-repeat="proposal in vm.proposals as results">
3 - <proposal-box proposal="proposal" topic="proposal.parent" category="proposal.parent.categories[0]" class="col-xs-12 col-sm-6"></proposal-box> 3 + <proposal-box proposal="proposal" topic="proposal.parent" category="proposal.parent.categories[0]" show-vote="false" class="col-xs-12 col-sm-6"></proposal-box>
4 <div ng-if="$odd" class="clearfix"></div> 4 <div ng-if="$odd" class="clearfix"></div>
5 </div> 5 </div>
6 <div class="animate-repeat" ng-if="results.length == 0"> 6 <div class="animate-repeat" ng-if="results.length == 0">
src/app/components/proposal-list/proposal-list.directive.js
@@ -31,7 +31,7 @@ @@ -31,7 +31,7 @@
31 31
32 vm.$timeout(function(){ 32 vm.$timeout(function(){
33 attachPopover.call(vm); 33 attachPopover.call(vm);
34 - }, 1000); 34 + }, 0);
35 }; 35 };
36 36
37 ProposalListController.prototype.showContent = function (proposal) { 37 ProposalListController.prototype.showContent = function (proposal) {
src/app/index.run.js
@@ -41,6 +41,7 @@ @@ -41,6 +41,7 @@
41 }); 41 });
42 42
43 $rootScope.currentUser = $localStorage.currentUser; 43 $rootScope.currentUser = $localStorage.currentUser;
  44 + $rootScope.temporaryToken = $localStorage.temporaryToken;
44 45
45 $log.debug('[RUN] Auth end.'); 46 $log.debug('[RUN] Auth end.');
46 } 47 }
src/app/layout.scss
@@ -9,7 +9,6 @@ @@ -9,7 +9,6 @@
9 display: table-cell; 9 display: table-cell;
10 float: none; 10 float: none;
11 height: 100%; 11 height: 100%;
12 - background-color: #181e21;  
13 } 12 }
14 13
15 .col-top { 14 .col-top {
@@ -206,6 +205,13 @@ @@ -206,6 +205,13 @@
206 margin-left: 10px; 205 margin-left: 10px;
207 padding: 0; 206 padding: 0;
208 border-radius: 100%; 207 border-radius: 100%;
  208 +
  209 + &:hover,
  210 + &:focus,
  211 + &:active {
  212 + color: #fff;
  213 + border-color: #fff;
  214 + }
209 } 215 }
210 216
211 ul.list-color { 217 ul.list-color {
@@ -224,7 +230,7 @@ ul.list-color li:before { @@ -224,7 +230,7 @@ ul.list-color li:before {
224 } 230 }
225 231
226 ul.list-color li:before { 232 ul.list-color li:before {
227 - content: ""; 233 + content: "\2022";
228 font-weight: bold; 234 font-weight: bold;
229 font-size: 20px; 235 font-size: 20px;
230 position: relative; 236 position: relative;
@@ -333,7 +339,7 @@ blockquote { @@ -333,7 +339,7 @@ blockquote {
333 position: relative; 339 position: relative;
334 margin: 0px; 340 margin: 0px;
335 border-left: none; 341 border-left: none;
336 - // line-height: 28px; 342 +
337 &:before { 343 &:before {
338 content: "\231C"; 344 content: "\231C";
339 font-size: 300px; 345 font-size: 300px;
src/app/pages/programas/programa.controller.js
@@ -82,7 +82,10 @@ @@ -82,7 +82,10 @@
82 } 82 }
83 83
84 vm.loadingTopProposals = true; 84 vm.loadingTopProposals = true;
85 - vm.DialogaService.getProposalsByTopicId(vm.article.id, {}, function(data) { 85 + vm.DialogaService.getProposalsByTopicId(vm.article.id, {
  86 + 'limit': 5
  87 + }, function(data) {
  88 + vm.total_proposals = parseInt(data._obj.headers('total'));
86 vm.proposals = data.articles; 89 vm.proposals = data.articles;
87 vm.proposalsTopFive = vm.proposals.slice(0, 5); 90 vm.proposalsTopFive = vm.proposals.slice(0, 5);
88 vm.proposalsTopRated = vm.proposals.slice(0, 3); 91 vm.proposalsTopRated = vm.proposals.slice(0, 3);
@@ -142,24 +145,6 @@ @@ -142,24 +145,6 @@
142 vm.proposalStatus = vm.PROPOSAL_STATUS.ERROR; 145 vm.proposalStatus = vm.PROPOSAL_STATUS.ERROR;
143 }); 146 });
144 }); 147 });
145 -  
146 - vm.$scope.$on('proposal-box:vote', function(event, params) {  
147 - // vm.$log.debug('event', event);  
148 - // vm.$log.debug('params', params);  
149 - var proposal_id = params.proposal_id;  
150 - var OPTION = params.OPTION;  
151 -  
152 - switch (OPTION){  
153 - case vm.VOTE_OPTIONS.UP:  
154 - case vm.VOTE_OPTIONS.DOWN:  
155 - case vm.VOTE_OPTIONS.SKIP:  
156 - vm.vote(proposal_id, OPTION);  
157 - break;  
158 - default:  
159 - vm.$log.error('Vote option not handled:', OPTION);  
160 - break;  
161 - }  
162 - });  
163 }; 148 };
164 149
165 ProgramaPageController.prototype.loadProposalById = function(proposal_id) { 150 ProgramaPageController.prototype.loadProposalById = function(proposal_id) {
@@ -219,31 +204,28 @@ @@ -219,31 +204,28 @@
219 return; 204 return;
220 } 205 }
221 206
222 - if (!vm.$rootScope.currentUser) {  
223 - // vm.$state.go('entrar', {  
224 - // redirect_uri: vm.sendProposalRedirectURI,  
225 - // message: 'Você precisa estar logado para votar em uma proposta.'  
226 - // }, {  
227 - // location: true  
228 - // }); 207 + if (!vm.$rootScope.temporaryToken) {
  208 + vm.$log.debug('"temporaryToken" not defined. Abort.');
229 return; 209 return;
230 } 210 }
231 211
232 vm.DialogaService.voteProposal(proposal_id, { 212 vm.DialogaService.voteProposal(proposal_id, {
233 value: value 213 value: value
234 - }, function(response) {  
235 - vm.$log.debug('response', response); 214 + }).then(function(response) {
  215 + vm.$log.debug('voteProposal response', response);
236 216
237 response.success = true; 217 response.success = true;
238 vm.$scope.$broadcast('proposal-box:vote-response', response); 218 vm.$scope.$broadcast('proposal-box:vote-response', response);
239 - }, function(error) {  
240 - vm.$log.error('error', error); 219 + }, function(response) {
  220 + vm.$log.debug('voteProposal error', response);
  221 +
  222 + response.error = true;
  223 + vm.$scope.$broadcast('proposal-box:vote-response', response);
  224 + }).finally(function(response){
  225 + vm.$log.debug('voteProposal finally', response);
241 226
242 - error.error = true;  
243 - vm.$scope.$broadcast('proposal-box:vote-response', error);  
244 }); 227 });
245 }; 228 };
246 - ProgramaPageController.prototype.voteHasBeenComputed = function() {};  
247 229
248 ProgramaPageController.prototype.showProposalsList = function() { 230 ProgramaPageController.prototype.showProposalsList = function() {
249 var vm = this; 231 var vm = this;
src/app/pages/programas/programa.html
@@ -90,7 +90,14 @@ @@ -90,7 +90,14 @@
90 <div> 90 <div>
91 <div class="col-xs-12" ng-if="!pagePrograma.loadingProposalBox && pagePrograma.randomProposal" ng-class="{'focused-proposal': !!pagePrograma.search.proposal_id}"> 91 <div class="col-xs-12" ng-if="!pagePrograma.loadingProposalBox && pagePrograma.randomProposal" ng-class="{'focused-proposal': !!pagePrograma.search.proposal_id}">
92 <h3 class="color-theme-fg">Apoie outras propostas</h3> 92 <h3 class="color-theme-fg">Apoie outras propostas</h3>
93 - <proposal-box proposal="pagePrograma.randomProposal" topic="pagePrograma.article" category="pagePrograma.category" can-vote="true" focus="{{pagePrograma.search.proposal_id}}" ></proposal-box> 93 + <proposal-box
  94 + proposal="pagePrograma.randomProposal"
  95 + topic="pagePrograma.article"
  96 + category="pagePrograma.category"
  97 + show-vote="true"
  98 + focus="{{pagePrograma.search.proposal_id}}"
  99 + do-vote="pagePrograma.vote(proposal_id, value)"
  100 + ></proposal-box>
94 </div> 101 </div>
95 102
96 <!-- Loading Proposal Box --> 103 <!-- Loading Proposal Box -->
@@ -133,7 +140,7 @@ @@ -133,7 +140,7 @@
133 <div class="row"> 140 <div class="row">
134 <div class="col-xs-12"> 141 <div class="col-xs-12">
135 <a ui-sref="ranking({tema: pagePrograma.category.slug, programa: pagePrograma.article.slug})" class="btn btn-link"> 142 <a ui-sref="ranking({tema: pagePrograma.category.slug, programa: pagePrograma.article.slug})" class="btn btn-link">
136 - <span ng-if="pagePrograma.proposals.length > 1">Veja todas as {{pagePrograma.proposals.length}} propostas</span> 143 + <span ng-if="pagePrograma.proposals.length > 1">Veja todas as {{pagePrograma.total_proposals}} propostas</span>
137 <span ng-if="pagePrograma.proposals.length === 1">Ir para a página de ranking</span> 144 <span ng-if="pagePrograma.proposals.length === 1">Ir para a página de ranking</span>
138 </a> 145 </a>
139 </div> 146 </div>
src/app/pages/programas/programas.controller.js
@@ -139,6 +139,21 @@ @@ -139,6 +139,21 @@
139 139
140 }; 140 };
141 141
  142 + ProgramasPageController.prototype.submitSearch = function() {
  143 + var vm = this;
  144 +
  145 + vm.loadingFilter = true;
  146 +
  147 + // scroll to result grid
  148 + var $searchResult = angular.element('#search-result');
  149 + if ($searchResult && $searchResult.length > 0) {
  150 + angular.element('body').animate({scrollTop: $searchResult.offset().top}, 'fast');
  151 + vm.filtredPrograms = vm.getFiltredPrograms();
  152 + }else {
  153 + vm.$log.warn('#search-result element not found.');
  154 + }
  155 + };
  156 +
142 ProgramasPageController.prototype.showAllPrograms = function($event) { 157 ProgramasPageController.prototype.showAllPrograms = function($event) {
143 var vm = this; 158 var vm = this;
144 $event.stopPropagation(); 159 $event.stopPropagation();
src/app/pages/programas/programas.html
@@ -26,7 +26,7 @@ @@ -26,7 +26,7 @@
26 <label for="articleQueryFilter" class="control-label sr-only">Buscar programas:</label> 26 <label for="articleQueryFilter" class="control-label sr-only">Buscar programas:</label>
27 <input id="articleQueryFilter" type="search" class="form-control input-search" ng-model="pageProgramas.query" placeholder="Buscar programas" aria-label="Buscar programas" > 27 <input id="articleQueryFilter" type="search" class="form-control input-search" ng-model="pageProgramas.query" placeholder="Buscar programas" aria-label="Buscar programas" >
28 <span class="input-group-btn"> 28 <span class="input-group-btn">
29 - <button type="button" class="btn btn-default" ng-click="pageProgramas.search()"> 29 + <button type="button" class="btn btn-default" ng-click="pageProgramas.submitSearch()">
30 <span class="icon-circle icon-small color-theme-common-bg"> 30 <span class="icon-circle icon-small color-theme-common-bg">
31 <span class="glyphicon glyphicon-search"></span> 31 <span class="glyphicon glyphicon-search"></span>
32 </span> 32 </span>
@@ -58,7 +58,7 @@ @@ -58,7 +58,7 @@
58 <label for="articleQueryFilter" class="control-label sr-only">Buscar programas:</label> 58 <label for="articleQueryFilter" class="control-label sr-only">Buscar programas:</label>
59 <input id="articleQueryFilter" type="search" class="form-control input-search" ng-model="pageProgramas.query" placeholder="Buscar programas" aria-label="Buscar programas" > 59 <input id="articleQueryFilter" type="search" class="form-control input-search" ng-model="pageProgramas.query" placeholder="Buscar programas" aria-label="Buscar programas" >
60 <span class="input-group-btn"> 60 <span class="input-group-btn">
61 - <button type="button" class="btn btn-default" ng-click="pageProgramas.search()"> 61 + <button type="button" class="btn btn-default" ng-click="pageProgramas.submitSearch()">
62 <span class="icon-circle icon-small color-theme-common-bg"> 62 <span class="icon-circle icon-small color-theme-common-bg">
63 <span class="glyphicon glyphicon-search"></span> 63 <span class="glyphicon glyphicon-search"></span>
64 </span> 64 </span>
@@ -68,7 +68,7 @@ @@ -68,7 +68,7 @@
68 </div> 68 </div>
69 </div> 69 </div>
70 </div> 70 </div>
71 - <div class="row"> 71 + <div id="search-result" class="row">
72 <div class="col-sm-12"> 72 <div class="col-sm-12">
73 <header class="header"> 73 <header class="header">
74 <h2>Conheça os programas</h2> 74 <h2>Conheça os programas</h2>
src/app/pages/propostas/propostas.controller.js
@@ -6,11 +6,12 @@ @@ -6,11 +6,12 @@
6 .controller('PropostasPageController', PropostasPageController); 6 .controller('PropostasPageController', PropostasPageController);
7 7
8 /** @ngInject */ 8 /** @ngInject */
9 - function PropostasPageController(DialogaService, $scope, $location, $filter, $log) { 9 + function PropostasPageController(DialogaService, $scope, $rootScope, $location, $filter, $log) {
10 var vm = this; 10 var vm = this;
11 11
12 vm.DialogaService = DialogaService; 12 vm.DialogaService = DialogaService;
13 vm.$scope = $scope; 13 vm.$scope = $scope;
  14 + vm.$rootScope = $rootScope;
14 vm.$location = $location; 15 vm.$location = $location;
15 vm.$filter = $filter; 16 vm.$filter = $filter;
16 vm.$log = $log; 17 vm.$log = $log;
@@ -18,6 +19,7 @@ @@ -18,6 +19,7 @@
18 vm.init(); 19 vm.init();
19 vm.loadData(); 20 vm.loadData();
20 // vm.attachListeners(); // attach listeners after load data (SYNC) 21 // vm.attachListeners(); // attach listeners after load data (SYNC)
  22 + vm.$rootScope.focusMainContent();
21 23
22 $log.debug('PropostasPageController'); 24 $log.debug('PropostasPageController');
23 } 25 }
@@ -156,4 +158,19 @@ @@ -156,4 +158,19 @@
156 return output; 158 return output;
157 }; 159 };
158 160
  161 + PropostasPageController.prototype.submitSearch = function() {
  162 + var vm = this;
  163 +
  164 + vm.loadingFilter = true;
  165 +
  166 + // scroll to result grid
  167 + var $searchResult = angular.element('#search-result');
  168 + if ($searchResult && $searchResult.length > 0) {
  169 + angular.element('body').animate({scrollTop: $searchResult.offset().top}, 'fast');
  170 + vm.filtredProposals = vm.getFiltredProposals();
  171 + }else {
  172 + vm.$log.warn('#search-result element not found.');
  173 + }
  174 + };
  175 +
159 })(); 176 })();
src/app/pages/propostas/propostas.html
@@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
6 </div> 6 </div>
7 </div> 7 </div>
8 8
9 -<div class="page--propostas"> 9 +<div class="page--propostas" role="main">
10 10
11 <section class="section-info" ng-if="pagePropostas.loading || pagePropostas.error"> 11 <section class="section-info" ng-if="pagePropostas.loading || pagePropostas.error">
12 <div class="container"> 12 <div class="container">
@@ -46,7 +46,7 @@ @@ -46,7 +46,7 @@
46 <label for="articleQueryFilter" class="control-label sr-only">Buscar propostas:</label> 46 <label for="articleQueryFilter" class="control-label sr-only">Buscar propostas:</label>
47 <input id="articleQueryFilter" type="search" class="form-control input-search" ng-model="pagePropostas.query" placeholder="Buscar propostas" aria-label="Buscar propostas" > 47 <input id="articleQueryFilter" type="search" class="form-control input-search" ng-model="pagePropostas.query" placeholder="Buscar propostas" aria-label="Buscar propostas" >
48 <span class="input-group-btn"> 48 <span class="input-group-btn">
49 - <button type="button" class="btn btn-default" ng-click="pagePropostas.search()"> 49 + <button type="button" class="btn btn-default" ng-click="pagePropostas.submitSearch()">
50 <span class="icon-circle icon-small color-theme-common-bg"> 50 <span class="icon-circle icon-small color-theme-common-bg">
51 <span class="glyphicon glyphicon-search"></span> 51 <span class="glyphicon glyphicon-search"></span>
52 </span> 52 </span>
@@ -78,7 +78,7 @@ @@ -78,7 +78,7 @@
78 <label for="articleQueryFilter" class="control-label sr-only">Buscar propostas:</label> 78 <label for="articleQueryFilter" class="control-label sr-only">Buscar propostas:</label>
79 <input id="articleQueryFilter" type="search" class="form-control input-search" ng-model="pagePropostas.query" placeholder="Buscar propostas" aria-label="Buscar propostas" > 79 <input id="articleQueryFilter" type="search" class="form-control input-search" ng-model="pagePropostas.query" placeholder="Buscar propostas" aria-label="Buscar propostas" >
80 <span class="input-group-btn"> 80 <span class="input-group-btn">
81 - <button type="button" class="btn btn-default" ng-click="pagePropostas.search()"> 81 + <button type="button" class="btn btn-default" ng-click="pagePropostas.submitSearch()">
82 <span class="icon-circle icon-small color-theme-common-bg"> 82 <span class="icon-circle icon-small color-theme-common-bg">
83 <span class="glyphicon glyphicon-search"></span> 83 <span class="glyphicon glyphicon-search"></span>
84 </span> 84 </span>
@@ -89,7 +89,7 @@ @@ -89,7 +89,7 @@
89 </div> 89 </div>
90 </div> 90 </div>
91 91
92 - <div class="row" ng-if="pagePropostas.proposals"> 92 + <div id="search-result" class="row" ng-if="pagePropostas.proposals">
93 <div class="col-sm-12"> 93 <div class="col-sm-12">
94 <header class="header"> 94 <header class="header">
95 <h2>Total de Propostas: "<b>{{pagePropostas.filtredProposals.length}} propostas</b>"</h2> 95 <h2>Total de Propostas: "<b>{{pagePropostas.filtredProposals.length}} propostas</b>"</h2>