Commit 6c43d39c7ccec7028709ff36197cd564c8dc0fb0
Exists in
master
and in
7 other branches
Merge branch 'feature-proposal-vote'
Showing
15 changed files
with
283 additions
and
136 deletions
Show diff stats
src/app/components/article-service/article.service.js
... | ... | @@ -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 | 166 | var url = service.apiArticles + proposal_id + '/vote'; |
167 | 167 | var paramsExtended = angular.extend({ |
168 | - private_token: $rootScope.currentUser.private_token | |
169 | - // private_token: 'e2198fdbcc20409f082829b4b5c0848e' | |
168 | + private_token: $rootScope.temporaryToken | |
170 | 169 | }, params); |
171 | 170 | |
172 | 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 | 176 | function getEvents (community_id, params) { | ... | ... |
src/app/components/auth/auth.service.js
... | ... | @@ -53,11 +53,7 @@ |
53 | 53 | } |
54 | 54 | |
55 | 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 | 57 | var encodedData = 'private_token=' + API.token; |
62 | 58 | encodedData += '&activation_code=' + code; |
63 | 59 | |
... | ... | @@ -77,12 +73,7 @@ |
77 | 73 | } |
78 | 74 | |
79 | 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 | 77 | var encodedData = 'code=' + code; |
87 | 78 | encodedData += '&password=' + newPassword; |
88 | 79 | encodedData += '&password_confirmation=' + newPasswordConfirmation; |
... | ... | @@ -103,7 +94,7 @@ |
103 | 94 | } |
104 | 95 | |
105 | 96 | function forgotPassword (data){ |
106 | - var url = PATH.host +'/api/v1/forgot_password'; | |
97 | + var url = PATH.host + '/api/v1/forgot_password'; | |
107 | 98 | var encodedData = ([ |
108 | 99 | 'value=' + data.login, |
109 | 100 | 'captcha_text=' + data.captcha_text, |
... | ... | @@ -145,9 +136,28 @@ |
145 | 136 | }, function(response) { |
146 | 137 | $log.debug('AuthService.login [FAIL] response', response); |
147 | 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 | 161 | function logout () { |
152 | 162 | |
153 | 163 | Session.destroy(); |
... | ... | @@ -167,28 +177,13 @@ |
167 | 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 | 180 | var service = { |
187 | 181 | register: register, |
188 | 182 | activate: activate, |
189 | 183 | changePassword: changePassword, |
190 | 184 | forgotPassword: forgotPassword, |
191 | 185 | login: login, |
186 | + loginCaptcha: loginCaptcha, | |
192 | 187 | logout: logout, |
193 | 188 | isAuthenticated: isAuthenticated, |
194 | 189 | isAuthorized: isAuthorized |
... | ... | @@ -207,7 +202,7 @@ |
207 | 202 | |
208 | 203 | service.create = function(data) { |
209 | 204 | |
210 | - $localStorage.currentUser = data; | |
205 | + $localStorage.currentUser = data.user; | |
211 | 206 | $log.debug('User session created.', $localStorage.currentUser); |
212 | 207 | |
213 | 208 | return $localStorage.currentUser; |
... | ... | @@ -224,6 +219,14 @@ |
224 | 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 | 230 | return service; |
228 | 231 | } |
229 | 232 | ... | ... |
src/app/components/proposal-box/proposal-box.directive.js
... | ... | @@ -9,30 +9,34 @@ |
9 | 9 | function proposalBox() { |
10 | 10 | |
11 | 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 | 13 | $log.debug('ProposalBoxController'); |
14 | 14 | |
15 | 15 | var vm = this; |
16 | 16 | vm.$scope = $scope; |
17 | 17 | vm.$rootScope = $rootScope; |
18 | 18 | vm.$state = $state; |
19 | + vm.$timeout = $timeout; | |
20 | + vm.$interval = $interval; | |
21 | + vm.$window = $window; | |
19 | 22 | vm.VOTE_STATUS = VOTE_STATUS; |
20 | 23 | vm.VOTE_OPTIONS = VOTE_OPTIONS; |
24 | + vm.AuthService = AuthService; | |
21 | 25 | vm.$log = $log; |
22 | 26 | |
23 | 27 | vm.init(); |
24 | 28 | vm.addListeners(); |
25 | 29 | } |
26 | 30 | |
27 | - ProposalBoxController.prototype.init = function () { | |
31 | + ProposalBoxController.prototype.init = function() { | |
28 | 32 | |
29 | 33 | var vm = this; |
30 | 34 | |
31 | - vm.canVote = vm.canVote || false; | |
35 | + vm.showVote = vm.showVote || false; | |
32 | 36 | vm.focus = vm.focus || false; |
33 | 37 | vm.STATE = null; |
34 | 38 | vm.errorOnSkip = false; |
35 | - vm.showAuthMessage = null; | |
39 | + vm.showCaptchaForm = null; | |
36 | 40 | vm.voteProposalRedirectURI = null; |
37 | 41 | |
38 | 42 | var slug = vm.topic.slug; |
... | ... | @@ -40,37 +44,52 @@ |
40 | 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 | 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 | 52 | vm.STATE = null; |
49 | 53 | } |
50 | 54 | |
51 | - if(data.error){ | |
55 | + if (data.error) { | |
52 | 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 | 62 | vm.STATE = vm.VOTE_STATUS.SUCCESS; |
63 | 63 | } |
64 | 64 | |
65 | - if(data.error) { | |
65 | + if (data.error) { | |
66 | 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 | 93 | var vm = this; |
75 | 94 | |
76 | 95 | vm.$state.go('programa', { |
... | ... | @@ -81,45 +100,106 @@ |
81 | 100 | }); |
82 | 101 | }; |
83 | 102 | |
84 | - ProposalBoxController.prototype.vote = function (value) { | |
103 | + ProposalBoxController.prototype.canVote = function() { | |
85 | 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 | 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 | 191 | var vm = this; |
112 | 192 | |
113 | 193 | vm.errorOnSkip = false; |
114 | 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 | 199 | vm.$log.debug('Sending vote'); |
120 | 200 | }; |
121 | 201 | |
122 | - ProposalBoxController.prototype.getSocialUrl = function () { | |
202 | + ProposalBoxController.prototype.getSocialUrl = function() { | |
123 | 203 | var vm = this; |
124 | 204 | |
125 | 205 | return vm.$state.href('programa', { |
... | ... | @@ -135,8 +215,9 @@ |
135 | 215 | proposal: '=', |
136 | 216 | topic: '=', |
137 | 217 | category: '=', |
138 | - canVote: '=', | |
139 | - focus: '@' | |
218 | + showVote: '=', | |
219 | + focus: '@', | |
220 | + doVote: '&' | |
140 | 221 | // @ -> Text binding / one-way binding |
141 | 222 | // = -> Direct model binding / two-way binding |
142 | 223 | // & -> Behaviour binding / Method binding | ... | ... |
src/app/components/proposal-box/proposal-box.html
... | ... | @@ -52,8 +52,10 @@ |
52 | 52 | </div> |
53 | 53 | <div class="feedback" ng-if="vm.STATE === vm.VOTE_STATUS.ERROR"> |
54 | 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 | 59 | </p> |
58 | 60 | </div> |
59 | 61 | </div> |
... | ... | @@ -65,18 +67,48 @@ |
65 | 67 | </div> |
66 | 68 | </div> |
67 | 69 | </div> |
68 | - <div ng-show="vm.showAuthMessage"> | |
70 | + <div ng-show="vm.showCaptchaForm"> | |
69 | 71 | <div class="proposal-message-panel"> |
70 | 72 | <div class="row"> |
71 | 73 | <div class="row-height"> |
72 | 74 | <div class="col-sm-12 col-height col-middle"> |
73 | 75 | <div class="inside inside-full-height"> |
74 | 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 | 112 | </div> |
81 | 113 | </div> |
82 | 114 | </div> |
... | ... | @@ -112,12 +144,12 @@ |
112 | 144 | <div class="proposal-box--content"> |
113 | 145 | <div class="proposal-box--content-inner">{{vm.proposal.abstract}}</div> |
114 | 146 | </div> |
115 | - <div ng-hide="vm.canVote" class="proposal-box--join"> | |
147 | + <div ng-hide="vm.showVote" class="proposal-box--join"> | |
116 | 148 | <button class="btn btn-link color-theme-common-fg" ng-click="vm.showContent(vm.topic.slug)"> |
117 | 149 | Participe |
118 | 150 | </button> |
119 | 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 | 153 | <div class="row"> |
122 | 154 | <div class="col-xs-4"> |
123 | 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 | 91 | .content { |
92 | 92 | color: #262626; |
93 | 93 | font-size: 24px; |
94 | + font-size: 2.4rem; | |
94 | 95 | font-weight: bold; |
95 | 96 | line-height: 24px; |
96 | 97 | padding: 10px 30px; |
98 | + | |
99 | + form { | |
100 | + font-size: 18px; | |
101 | + font-size: 1.8rem; | |
102 | + } | |
97 | 103 | } |
98 | 104 | |
99 | 105 | .message-icon { |
... | ... | @@ -108,11 +114,13 @@ |
108 | 114 | |
109 | 115 | &--title { |
110 | 116 | font-size: 22px; |
117 | + font-size: 2.2rem; | |
111 | 118 | font-weight: bold; |
112 | 119 | } |
113 | 120 | |
114 | 121 | &--message { |
115 | 122 | font-size: 14px; |
123 | + font-size: 1.4rem; | |
116 | 124 | font-weight: normal; |
117 | 125 | line-height: 20px; |
118 | 126 | margin-top: 48px; | ... | ... |
src/app/components/proposal-grid/proposal-grid.html
1 | 1 | <div class="proposal-grid row"> |
2 | 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 | 4 | <div ng-if="$odd" class="clearfix"></div> |
5 | 5 | </div> |
6 | 6 | <div class="animate-repeat" ng-if="results.length == 0"> | ... | ... |
src/app/components/proposal-list/proposal-list.directive.js
src/app/index.run.js
src/app/layout.scss
... | ... | @@ -9,7 +9,6 @@ |
9 | 9 | display: table-cell; |
10 | 10 | float: none; |
11 | 11 | height: 100%; |
12 | - background-color: #181e21; | |
13 | 12 | } |
14 | 13 | |
15 | 14 | .col-top { |
... | ... | @@ -206,6 +205,13 @@ |
206 | 205 | margin-left: 10px; |
207 | 206 | padding: 0; |
208 | 207 | border-radius: 100%; |
208 | + | |
209 | + &:hover, | |
210 | + &:focus, | |
211 | + &:active { | |
212 | + color: #fff; | |
213 | + border-color: #fff; | |
214 | + } | |
209 | 215 | } |
210 | 216 | |
211 | 217 | ul.list-color { |
... | ... | @@ -224,7 +230,7 @@ ul.list-color li:before { |
224 | 230 | } |
225 | 231 | |
226 | 232 | ul.list-color li:before { |
227 | - content: "• "; | |
233 | + content: "\2022"; | |
228 | 234 | font-weight: bold; |
229 | 235 | font-size: 20px; |
230 | 236 | position: relative; |
... | ... | @@ -333,7 +339,7 @@ blockquote { |
333 | 339 | position: relative; |
334 | 340 | margin: 0px; |
335 | 341 | border-left: none; |
336 | - // line-height: 28px; | |
342 | + | |
337 | 343 | &:before { |
338 | 344 | content: "\231C"; |
339 | 345 | font-size: 300px; | ... | ... |
src/app/pages/programas/programa.controller.js
... | ... | @@ -82,7 +82,10 @@ |
82 | 82 | } |
83 | 83 | |
84 | 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 | 89 | vm.proposals = data.articles; |
87 | 90 | vm.proposalsTopFive = vm.proposals.slice(0, 5); |
88 | 91 | vm.proposalsTopRated = vm.proposals.slice(0, 3); |
... | ... | @@ -142,24 +145,6 @@ |
142 | 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 | 150 | ProgramaPageController.prototype.loadProposalById = function(proposal_id) { |
... | ... | @@ -219,31 +204,28 @@ |
219 | 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 | 209 | return; |
230 | 210 | } |
231 | 211 | |
232 | 212 | vm.DialogaService.voteProposal(proposal_id, { |
233 | 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 | 217 | response.success = true; |
238 | 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 | 230 | ProgramaPageController.prototype.showProposalsList = function() { |
249 | 231 | var vm = this; | ... | ... |
src/app/pages/programas/programa.html
... | ... | @@ -90,7 +90,14 @@ |
90 | 90 | <div> |
91 | 91 | <div class="col-xs-12" ng-if="!pagePrograma.loadingProposalBox && pagePrograma.randomProposal" ng-class="{'focused-proposal': !!pagePrograma.search.proposal_id}"> |
92 | 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 | 101 | </div> |
95 | 102 | |
96 | 103 | <!-- Loading Proposal Box --> |
... | ... | @@ -133,7 +140,7 @@ |
133 | 140 | <div class="row"> |
134 | 141 | <div class="col-xs-12"> |
135 | 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 | 144 | <span ng-if="pagePrograma.proposals.length === 1">Ir para a página de ranking</span> |
138 | 145 | </a> |
139 | 146 | </div> | ... | ... |
src/app/pages/programas/programas.controller.js
... | ... | @@ -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 | 157 | ProgramasPageController.prototype.showAllPrograms = function($event) { |
143 | 158 | var vm = this; |
144 | 159 | $event.stopPropagation(); | ... | ... |
src/app/pages/programas/programas.html
... | ... | @@ -26,7 +26,7 @@ |
26 | 26 | <label for="articleQueryFilter" class="control-label sr-only">Buscar programas:</label> |
27 | 27 | <input id="articleQueryFilter" type="search" class="form-control input-search" ng-model="pageProgramas.query" placeholder="Buscar programas" aria-label="Buscar programas" > |
28 | 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 | 30 | <span class="icon-circle icon-small color-theme-common-bg"> |
31 | 31 | <span class="glyphicon glyphicon-search"></span> |
32 | 32 | </span> |
... | ... | @@ -58,7 +58,7 @@ |
58 | 58 | <label for="articleQueryFilter" class="control-label sr-only">Buscar programas:</label> |
59 | 59 | <input id="articleQueryFilter" type="search" class="form-control input-search" ng-model="pageProgramas.query" placeholder="Buscar programas" aria-label="Buscar programas" > |
60 | 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 | 62 | <span class="icon-circle icon-small color-theme-common-bg"> |
63 | 63 | <span class="glyphicon glyphicon-search"></span> |
64 | 64 | </span> |
... | ... | @@ -68,7 +68,7 @@ |
68 | 68 | </div> |
69 | 69 | </div> |
70 | 70 | </div> |
71 | - <div class="row"> | |
71 | + <div id="search-result" class="row"> | |
72 | 72 | <div class="col-sm-12"> |
73 | 73 | <header class="header"> |
74 | 74 | <h2>Conheça os programas</h2> | ... | ... |
src/app/pages/propostas/propostas.controller.js
... | ... | @@ -6,11 +6,12 @@ |
6 | 6 | .controller('PropostasPageController', PropostasPageController); |
7 | 7 | |
8 | 8 | /** @ngInject */ |
9 | - function PropostasPageController(DialogaService, $scope, $location, $filter, $log) { | |
9 | + function PropostasPageController(DialogaService, $scope, $rootScope, $location, $filter, $log) { | |
10 | 10 | var vm = this; |
11 | 11 | |
12 | 12 | vm.DialogaService = DialogaService; |
13 | 13 | vm.$scope = $scope; |
14 | + vm.$rootScope = $rootScope; | |
14 | 15 | vm.$location = $location; |
15 | 16 | vm.$filter = $filter; |
16 | 17 | vm.$log = $log; |
... | ... | @@ -18,6 +19,7 @@ |
18 | 19 | vm.init(); |
19 | 20 | vm.loadData(); |
20 | 21 | // vm.attachListeners(); // attach listeners after load data (SYNC) |
22 | + vm.$rootScope.focusMainContent(); | |
21 | 23 | |
22 | 24 | $log.debug('PropostasPageController'); |
23 | 25 | } |
... | ... | @@ -156,4 +158,19 @@ |
156 | 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 | 6 | </div> |
7 | 7 | </div> |
8 | 8 | |
9 | -<div class="page--propostas"> | |
9 | +<div class="page--propostas" role="main"> | |
10 | 10 | |
11 | 11 | <section class="section-info" ng-if="pagePropostas.loading || pagePropostas.error"> |
12 | 12 | <div class="container"> |
... | ... | @@ -46,7 +46,7 @@ |
46 | 46 | <label for="articleQueryFilter" class="control-label sr-only">Buscar propostas:</label> |
47 | 47 | <input id="articleQueryFilter" type="search" class="form-control input-search" ng-model="pagePropostas.query" placeholder="Buscar propostas" aria-label="Buscar propostas" > |
48 | 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 | 50 | <span class="icon-circle icon-small color-theme-common-bg"> |
51 | 51 | <span class="glyphicon glyphicon-search"></span> |
52 | 52 | </span> |
... | ... | @@ -78,7 +78,7 @@ |
78 | 78 | <label for="articleQueryFilter" class="control-label sr-only">Buscar propostas:</label> |
79 | 79 | <input id="articleQueryFilter" type="search" class="form-control input-search" ng-model="pagePropostas.query" placeholder="Buscar propostas" aria-label="Buscar propostas" > |
80 | 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 | 82 | <span class="icon-circle icon-small color-theme-common-bg"> |
83 | 83 | <span class="glyphicon glyphicon-search"></span> |
84 | 84 | </span> |
... | ... | @@ -89,7 +89,7 @@ |
89 | 89 | </div> |
90 | 90 | </div> |
91 | 91 | |
92 | - <div class="row" ng-if="pagePropostas.proposals"> | |
92 | + <div id="search-result" class="row" ng-if="pagePropostas.proposals"> | |
93 | 93 | <div class="col-sm-12"> |
94 | 94 | <header class="header"> |
95 | 95 | <h2>Total de Propostas: "<b>{{pagePropostas.filtredProposals.length}} propostas</b>"</h2> | ... | ... |