Commit 427d64e0d04f6fdb24f4ce22ecfbcb9981cacb3e

Authored by Leonardo Merlin
1 parent 252bbec3

Sync

src/app/components/proposal-box/proposal-box.directive.js
... ... @@ -32,6 +32,8 @@
32 32 vm.focus = vm.focus || false;
33 33 vm.STATE = null;
34 34 vm.errorOnSkip = false;
  35 + vm.showAuthMessage = null;
  36 + vm.voteProposalRedirectURI = null;
35 37 };
36 38  
37 39 ProposalBoxController.prototype.addListeners = function () {
... ... @@ -43,9 +45,10 @@
43 45 }
44 46  
45 47 if(data.error){
46   - vm.errorOnSkip = true;
  48 + vm.errorOnSkip = data.error;
47 49 }
48 50 });
  51 +
49 52 vm.$scope.$on('proposal-box:vote-response', function(event, data){
50 53 vm.$log.debug('proposal-box:vote-response');
51 54 vm.$log.debug('event', event);
... ... @@ -57,10 +60,18 @@
57 60  
58 61 if(data.error) {
59 62 vm.STATE = vm.VOTE_STATUS.ERROR;
60   -
61 63 }
  64 +
62 65 vm.message = data.message;
63 66 });
  67 +
  68 + vm.$scope.$watch('vm.voteProposalRedirectURI', function(newValue, oldValue){
  69 + if(newValue && newValue !== oldValue){
  70 + var slug = vm.topic.slug;
  71 + var proposal_id = vm.proposal.id;
  72 + vm.voteProposalRedirectURI = 'state=programa&task=vote-proposal&slug=' + slug + '&proposal_id=' + proposal_id;
  73 + }
  74 + });
64 75 };
65 76  
66 77 ProposalBoxController.prototype.showContent = function (slug) {
... ... @@ -85,7 +96,7 @@
85 96 vm.$log.debug('Sending vote', value);
86 97 }else{
87 98 vm.$log.info('Must be logged in...');
88   -
  99 + vm.showAuthMessage = true;
89 100 }
90 101 };
91 102  
... ...
src/app/components/proposal-box/proposal-box.html
1 1 <div class="proposal-box" ng-class="[{'focus': (vm.focus || (vm.STATE === vm.VOTE_STATUS.LOADING) )}, vm.category.slug]">
2   - <div ng-show="(vm.STATE === vm.VOTE_STATUS.SUCCESS) || (vm.STATE === vm.VOTE_STATUS.ERROR)">
3   - FEEDBACK
4   - </div>
5   - <div ng-hide="(vm.STATE === vm.VOTE_STATUS.SUCCESS) || (vm.STATE === vm.VOTE_STATUS.ERROR)">
6   - <div ng-show="vm.STATE === vm.VOTE_STATUS.LOADING">
7   - <div class="proposal-loading">
8   - <div class="shadow"></div>
9   - <div class="text text-center" ng-show="!vm.errorOnSkip">
10   - Carregando...
  2 + <!-- <div ng-hide="(vm.STATE === vm.VOTE_STATUS.SUCCESS) || (vm.STATE === vm.VOTE_STATUS.ERROR)"> -->
  3 + <div>
  4 + <div ng-show="(vm.STATE === vm.VOTE_STATUS.SUCCESS) || (vm.STATE === vm.VOTE_STATUS.ERROR)">
  5 + <div class="proposal-message-panel">
  6 + <div class="row">
  7 + <div class="row-height">
  8 + <div class="col-sm-12 col-sm-height col-middle">
  9 + <div class="inside inside-full-height">
  10 + <div class="content text-center show-message">
  11 + <div class="row">
  12 + <div class="col-xs-12">
  13 + <div class="message-icon color-fg-white" ng-class="{'success': (vm.STATE === vm.VOTE_STATUS.SUCCESS), 'error': vm.STATE === vm.VOTE_STATUS.ERROR}">
  14 + <span ng-if="vm.STATE === vm.VOTE_STATUS.SUCCESS" class="glyphicon glyphicon-ok" aria-hidden="true"></span>
  15 + <span ng-if="vm.STATE === vm.VOTE_STATUS.ERROR" class="glyphicon glyphicon-remove" aria-hidden="true"></span>
  16 + </div>
  17 + </div>
  18 + </div>
  19 + <div class="row">
  20 + <div class="col-xs-12">
  21 + <p style="padding-top:20px;">{{vm.message}}</p>
  22 + </div>
  23 + </div>
  24 + </div>
  25 + </div>
  26 + </div>
  27 + </div>
11 28 </div>
12   - <div class="text" ng-show="vm.errorOnSkip">
13   - Erro ao carregar nova proposta.
  29 + </div>
  30 + </div>
  31 + <div ng-show="vm.showAuthMessage">
  32 + <div class="proposal-message-panel">
  33 + <div class="row">
  34 + <div class="row-height">
  35 + <div class="col-sm-12 col-sm-height col-middle">
  36 + <div class="inside inside-full-height">
  37 + <div class="content text-center">
  38 + <p>Você precisa estar logado para votar na proposta</p>
  39 + <br>
  40 + <p>
  41 + <!-- <a ui-sref="entrar({redirect_uri: vm.voteProposalRedirectURI})">Clique aqui</a> para ir para a página de login. -->
  42 + <a ui-sref="entrar({redirect_uri: vm.voteProposalRedirectURI})">Clique aqui para ir para a página de login</a>
  43 + </p>
  44 + </div>
  45 + </div>
  46 + </div>
  47 + </div>
  48 + </div>
  49 + </div>
  50 + </div>
  51 + <div ng-show="vm.STATE === vm.VOTE_STATUS.LOADING">
  52 + <div class="proposal-message-panel">
  53 + <div class="row">
  54 + <div class="row-height">
  55 + <div class="col-sm-12 col-sm-height col-middle">
  56 + <div class="inside inside-full-height">
  57 + <div class="content text-center">
  58 + <div ng-show="!vm.errorOnSkip">
  59 + <p>Carregando...</p>
  60 + </div>
  61 + <div ng-show="!vm.errorOnSkip">
  62 + <p>Erro ao carregar nova proposta proposta.</p>
  63 + <p>{{vm.errorOnSkip}}</p>
  64 + </div>
  65 + </div>
  66 + </div>
  67 + </div>
  68 + </div>
14 69 </div>
15 70 </div>
16 71 </div>
... ... @@ -20,43 +75,43 @@
20 75 </div>
21 76 <div class="proposal-box--middle">
22 77 <div class="proposal-box--content">
23   - <div class="proposal-box--content-inner">{{vm.proposal.abstract}}</div>
24   - </div>
25   - <div ng-hide="vm.canVote" class="proposal-box--join">
26   - <button class="btn btn-link color-theme-common-fg" ng-click="vm.showContent(vm.topic.slug)">
27   - Participe
28   - <span class="glyphicon glyphicon-menu-right color-theme-common-fg" aria-hidde="true"></span>
29   - </button>
30   - </div>
31   - <div ng-show="vm.canVote" class="proposal-box--actions text-center">
32   - <div class="row">
33   - <div class="col-xs-4">
34   - <div class="action vote_for" ng-click="vm.vote(vm.VOTE_OPTIONS.UP)">
35   - <div class="icon-circle">
36   - <span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
  78 + <div class="proposal-box--content-inner">{{vm.proposal.abstract}}</div>
  79 + </div>
  80 + <div ng-hide="vm.canVote" class="proposal-box--join">
  81 + <button class="btn btn-link color-theme-common-fg" ng-click="vm.showContent(vm.topic.slug)">
  82 + Participe
  83 + <span class="glyphicon glyphicon-menu-right color-theme-common-fg" aria-hidde="true"></span>
  84 + </button>
  85 + </div>
  86 + <div ng-show="vm.canVote" class="proposal-box--actions text-center">
  87 + <div class="row">
  88 + <div class="col-xs-4">
  89 + <div class="action vote_for" ng-click="vm.vote(vm.VOTE_OPTIONS.UP)">
  90 + <div class="icon-circle">
  91 + <span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
  92 + </div>
  93 + <div class="action-label">Apoio</div>
37 94 </div>
38   - <div class="action-label">Apoio</div>
39 95 </div>
40   - </div>
41   - <div class="col-xs-4">
42   - <div class="action skip" ng-click="vm.skip()">
43   - <div class="icon-circle">
44   - <span class="glyphicon glyphicon-share-alt" aria-hidden="true"></span>
  96 + <div class="col-xs-4">
  97 + <div class="action skip" ng-click="vm.skip()">
  98 + <div class="icon-circle">
  99 + <span class="glyphicon glyphicon-share-alt" aria-hidden="true"></span>
  100 + </div>
  101 + <div class="action-label">Pular</div>
45 102 </div>
46   - <div class="action-label">Pular</div>
47 103 </div>
48   - </div>
49   - <div class="col-xs-4">
50   - <div class="action vote_against" ng-click="vm.vote(vm.VOTE_OPTIONS.DOWN)">
51   - <div class="icon-circle">
52   - <span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
  104 + <div class="col-xs-4">
  105 + <div class="action vote_against" ng-click="vm.vote(vm.VOTE_OPTIONS.DOWN)">
  106 + <div class="icon-circle">
  107 + <span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
  108 + </div>
  109 + <div class="action-label">Não Apoio</div>
53 110 </div>
54   - <div class="action-label">Não Apoio</div>
55 111 </div>
56 112 </div>
57 113 </div>
58 114 </div>
59   - </div>
60 115 <div class="proposal-box--bottom text-center">
61 116 <div class="proposal-box--share">
62 117 <span>COMPARTILHE ESSA <b>PROPOSTA</b></span>
... ... @@ -66,12 +121,12 @@
66 121 </div>
67 122 </div>
68 123 <div class="proposal-box--ranking">
69   - <div class="proposal-box--ranking-inner">
70   - <span class="icon icon-small icon-ranking" aria-hidden="true"></span>
71   - <span>Colocação nos resultados:</span>
72   - <span>{{vm.proposal.ranking_position}}º</span>
73   - </div>
74   - </div>
  124 + <div class="proposal-box--ranking-inner">
  125 + <span class="icon icon-small icon-ranking" aria-hidden="true"></span>
  126 + <span>Colocação nos resultados:</span>
  127 + <span>{{vm.proposal.ranking_position}}º</span>
  128 + </div>
  129 + </div>
75 130 </div>
76 131 </div>
77 132 </div>
... ...
src/app/components/proposal-box/proposal-box.scss
... ... @@ -72,29 +72,25 @@
72 72 }
73 73 }
74 74  
75   - .proposal-loading {
76   -
77   - .text {
78   - position: absolute;
79   - color: #fff;
  75 + .proposal-message-panel {
  76 + position: absolute;
  77 + width: 100%;
  78 + height: 100%;
  79 + top: 0;
  80 + left: 0;
  81 + z-index: 1000;
  82 + background-color: #f5f5f5;
  83 + background-color: rgba(245, 245, 245, 0.9);
  84 +
  85 + .row { height: 100%; }
  86 + .inside { position: relative; }
  87 +
  88 + .content {
  89 + color: #262626;
80 90 font-size: 24px;
  91 + font-weight: bold;
81 92 line-height: 24px;
82   - z-index: 1000;
83   - width: 100%
84   - top: 50%;
85   - left: 0;
86   - margin-top: -12px;
87   - }
88   -
89   - .shadow {
90   - content: "";
91   - position: absolute;
92   - z-index: 999;
93   - width: 100%;
94   - height: 100%;
95   - top: 0;
96   - left: 0;
97   - background-color: rgba(0,0,0,.4);
  93 + padding: 10px 30px;
98 94 }
99 95 }
100 96  
... ...
src/app/components/util-service/utils.service.js
... ... @@ -72,7 +72,7 @@
72 72 }
73 73  
74 74 // Otherwise, use expected error message.
75   - return $q.reject(error.data.message);
  75 + return $q.reject(error.data);
76 76 }
77 77 }
78 78 })();
... ...
src/app/index.constants.js
... ... @@ -38,9 +38,9 @@
38 38 LOADING: 0x100
39 39 })
40 40 .constant('VOTE_OPTIONS', {
41   - UP: 0x1,
42   - DOWN: 0x10,
43   - SKIP: 0x100
  41 + UP: 1,
  42 + DOWN: -1,
  43 + SKIP: 0
44 44 })
45 45 .constant('USER_ROLES', {
46 46 all: '*',
... ...
src/app/index.route.js
... ... @@ -23,7 +23,7 @@
23 23 }
24 24 })
25 25 .state('entrar', {
26   - url: '/entrar?redirect_uri',
  26 + url: '/entrar?redirect_uri&message',
27 27 ncyBreadcrumb: {label: 'Entrar'},
28 28 views: {
29 29 'header': { templateUrl: 'app/pages/header/header.html' },
... ...
src/app/pages/auth/auth.controller.js
... ... @@ -159,7 +159,8 @@
159 159 case 'programa':
160 160 vm.$state.go(state, {
161 161 slug: vm.params.slug,
162   - task: vm.params.task
  162 + task: vm.params.task,
  163 + proposal_id: vm.params.proposal_id,
163 164 });
164 165 break;
165 166 default:
... ...
src/app/pages/auth/signin.html
... ... @@ -152,9 +152,8 @@
152 152 <div class="checkbox">
153 153 <label for="user_terms_accepted">
154 154 <input type="checkbox" id="user_terms_accepted" name="user_terms_accepted" value="aceito" ng-model="pageSignin.signup.user_terms_accepted" required>
155   - Já li os
  155 + Já li e concordo com os
156 156 <button type="button" class="btn btn-link" style="padding:0 0 4px 0;" data-toggle="modal" data-target="#modalTermosDeUso">Termos de Uso</button>
157   - e concordo com os mesmos*
158 157 </label>
159 158 </div>
160 159 </input>
... ...
src/app/pages/inicio/inicio.html
... ... @@ -135,7 +135,7 @@
135 135 <header class="header">
136 136 <h2>Programas</h2>
137 137 <button type="button" class="btn btn-link" ng-click="pageInicio.showAllPrograms($event)">
138   - <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span> Ver todos os {{::pageInicio.programs.length}} programas
  138 + <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span> Ver todos os programas
139 139 </button>
140 140 </header>
141 141 </div>
... ...
src/app/pages/programas/programa.controller.js
... ... @@ -6,13 +6,14 @@
6 6 .controller('ProgramaPageController', ProgramaPageController);
7 7  
8 8 /** @ngInject */
9   - function ProgramaPageController(DialogaService, PATH, $state, $location, $scope, $rootScope, $element, $timeout, $log) {
  9 + function ProgramaPageController(DialogaService, PATH, VOTE_OPTIONS, $state, $location, $scope, $rootScope, $element, $timeout, $log) {
10 10 $log.debug('ProgramaPageController');
11 11  
12 12 var vm = this;
13 13  
14 14 vm.DialogaService = DialogaService;
15 15 vm.PATH = PATH;
  16 + vm.VOTE_OPTIONS = VOTE_OPTIONS;
16 17 vm.$state = $state;
17 18 vm.$location = $location;
18 19 vm.$scope = $scope;
... ... @@ -31,7 +32,11 @@
31 32  
32 33 vm.article = null;
33 34 vm.category = null;
  35 + vm.loading = null;
  36 + vm.loadingTopProposals = null;
  37 + vm.loadingProposalBox = null;
34 38 vm.sendProposalRedirectURI = null;
  39 + // vm.voteProposalRedirectURI = null;
35 40 vm.search = vm.$location.search();
36 41  
37 42 vm.error = false;
... ... @@ -45,7 +50,7 @@
45 50 // Get program by slug
46 51 var slug = vm.$state.params.slug;
47 52  
48   - if(!slug){
  53 + if (!slug) {
49 54 vm.$log.error('slug not defined.');
50 55 vm.$log.info('Rollback to home page.');
51 56 vm.$state.go('inicio', {}, {location: true});
... ... @@ -55,6 +60,7 @@
55 60 vm.article = article;
56 61 vm.category = vm.article.categories[0];
57 62 vm.sendProposalRedirectURI = 'state=programa&task=send-proposal&slug=' + slug;
  63 + // vm.voteProposalRedirectURI = 'state=programa&task=vote-proposal&slug=' + slug;
58 64  
59 65 // update the breadcrumb
60 66 vm.$rootScope.contentTitle = vm.article.title;
... ... @@ -67,43 +73,23 @@
67 73 };
68 74 }
69 75  
70   - vm.DialogaService.getProposalsByTopicId(vm.article.id, {}, function(data){
  76 + vm.loadingTopProposals = true;
  77 + vm.DialogaService.getProposalsByTopicId(vm.article.id, {}, function(data) {
71 78 vm.proposals = data.articles;
72 79 vm.proposalsTopRated = vm.proposals.slice(0, 3);
73   - }, function (error) {
  80 + vm.loadingTopProposals = false;
  81 + }, function(error) {
74 82 vm.$log.error(error);
  83 + vm.loadingTopProposals = false;
75 84 });
76 85  
77   - if(vm.search.proposal_id){
78   - var proposalUrlId = vm.search.proposal_id;
79   - vm.DialogaService.getProposalById(proposalUrlId, {
80   - 'limit': '1'
81   - }, _handleSuccessGetProposal, _handleErrorGetProposal);
82   -
83   - }else{
84   - // get random proposal
85   - vm.DialogaService.getProposalsByTopicId(vm.article.id, {
86   - 'order': 'random()',
87   - 'limit': '1'
88   - }, _handleSuccessGetProposal, _handleErrorGetProposal);
89   - }
90   -
91   - function _handleSuccessGetProposal(data){
92   - if(data && data.articles){
93   - vm.randomProposal = data.articles[0];
94   - }
95 86  
96   - // scroll to focused proposal
97   - if(vm.search.proposal_id){
98   - vm.$timeout(function(){
99   - var target = angular.element('.focused-proposal');
100   - angular.element('body').animate({scrollTop: target.offset().top}, 'fast');
101   - }, 300);
102   - }
103   - }
104   -
105   - function _handleErrorGetProposal(error){
106   - vm.$log.error(error);
  87 + vm.loadingProposalBox = true;
  88 + if (vm.search.proposal_id) {
  89 + vm.loadProposalById(vm.search.proposal_id);
  90 + }else {
  91 + // random proposal
  92 + vm.loadRandomProposal();
107 93 }
108 94  
109 95 vm.loading = false;
... ... @@ -111,9 +97,6 @@
111 97 vm.$log.error(error);
112 98 vm.error = error;
113 99 vm.loading = false;
114   -
115   - // vm.$log.info('Rollback to home page.');
116   - // vm.$state.go('inicio', {}, {location: true});
117 100 });
118 101 };
119 102  
... ... @@ -125,21 +108,117 @@
125 108 });
126 109  
127 110 vm.$scope.$on('cadastro-proposa:startSendProposal', function(event, proposal) {
128   - // vm.$log.debug('proposal', proposal);
129 111 vm.creatingProposal = true;
130   - vm.DialogaService.createProposal(proposal, vm.article.id, function (response){
  112 + vm.DialogaService.createProposal(proposal, vm.article.id, function(response) {
131 113 vm.$log.debug('response', response);
132 114 vm.creatingProposal = false;
133   - }, function (error) {
  115 + }, function(error) {
134 116 vm.$log.error(error);
135 117 vm.creatingProposal = false;
136 118 });
137 119 });
138 120  
139 121 vm.$scope.$on('proposal-box:vote', function(event, params) {
  122 + // vm.$log.debug('event', event);
  123 + // vm.$log.debug('params', params);
  124 + var proposal_id = params.proposal_id;
  125 + var OPTION = params.OPTION;
  126 +
  127 + switch (OPTION){
  128 + case vm.VOTE_OPTIONS.UP:
  129 + case vm.VOTE_OPTIONS.DOWN:
  130 + case vm.VOTE_OPTIONS.SKIP:
  131 + vm.vote(proposal_id, OPTION);
  132 + break;
  133 + default:
  134 + vm.$log.error('Vote option not handled:', OPTION);
  135 + break;
  136 + }
  137 + });
  138 + };
  139 +
  140 + ProgramaPageController.prototype.loadProposalById = function(proposal_id) {
  141 + var vm = this;
  142 +
  143 + vm.DialogaService.getProposalById(proposal_id, {
  144 + 'limit': '1'
  145 + }, vm._handleSuccessOnGetProposal.bind(vm), vm._handleErrorOnGetProposal.bind(vm));
  146 + };
  147 +
  148 + ProgramaPageController.prototype.loadRandomProposal = function() {
  149 + var vm = this;
  150 +
  151 + vm.DialogaService.getProposalsByTopicId(vm.article.id, {
  152 + 'order': 'random()',
  153 + 'limit': '1'
  154 + }, vm._handleSuccessOnGetProposal.bind(vm), vm._handleErrorOnGetProposal.bind(vm));
  155 + };
  156 +
  157 + ProgramaPageController.prototype._handleSuccessOnGetProposal = function(data) {
  158 + var vm = this;
  159 +
  160 + if (data && data.articles) {
  161 + var MAX = data.articles.length;
  162 + vm.randomProposal = data.articles[Math.floor(Math.random() * MAX)];
  163 + vm.loadingProposalBox = false;
  164 + vm.$scope.$broadcast('proposal-box:proposal-loaded', { success: true});
  165 + }
  166 +
  167 + // scroll to focused proposal
  168 + if (vm.search.proposal_id) {
  169 + vm.$timeout(function() {
  170 + var target = angular.element('.focused-proposal');
  171 + if (target && target.length > 0) {
  172 + angular.element('body').animate({scrollTop: target.offset().top}, 'fast');
  173 + }
  174 + }, 300);
  175 + }
  176 + };
  177 +
  178 + ProgramaPageController.prototype._handleErrorOnGetProposal = function(error) {
  179 + var vm = this;
  180 + vm.$log.error(error);
  181 + vm.$scope.$broadcast('proposal-box:proposal-loaded', { error: true});
  182 + };
  183 +
  184 + ProgramaPageController.prototype.voteSkip = function() {
  185 + var vm = this;
  186 + vm.loadRandomProposal();
  187 + };
  188 +
  189 + ProgramaPageController.prototype.vote = function(proposal_id, value) {
  190 + var vm = this;
  191 +
  192 + if (value === vm.VOTE_OPTIONS.SKIP) {
  193 + vm.voteSkip();
  194 + return;
  195 + }
  196 +
  197 + if (!vm.$rootScope.currentUser) {
  198 + // vm.$state.go('entrar', {
  199 + // redirect_uri: vm.sendProposalRedirectURI,
  200 + // message: 'Você precisa estar logado para votar em uma proposta.'
  201 + // }, {
  202 + // location: true
  203 + // });
  204 + return;
  205 + }
  206 +
  207 + vm.DialogaService.voteProposal(proposal_id, {
  208 + value: value
  209 + }, function(response) {
  210 + vm.$log.debug('response', response);
  211 +
  212 + response.success = true;
  213 + vm.$scope.$broadcast('proposal-box:vote-response', response);
  214 + }, function(error) {
  215 + vm.$log.error('error', error);
140 216  
  217 + error.error = true;
  218 + vm.$scope.$broadcast('proposal-box:vote-response', error);
141 219 });
142 220 };
  221 + ProgramaPageController.prototype.voteHasBeenComputed = function() {};
143 222  
144 223 ProgramaPageController.prototype.showProposalsList = function() {
145 224 var vm = this;
... ...
src/app/pages/programas/programa.html
... ... @@ -23,9 +23,11 @@
23 23 <div class="container">
24 24 <div class="row">
25 25 <article class="program-preview">
  26 + <!-- Preview > Titulo -->
26 27 <div class="col-md-12">
27 28 <h1 class="program-preview--title color-theme-fg">{{::pagePrograma.article.title}}</h1>
28 29 </div>
  30 + <!-- Preview > coluna da esquerda -->
29 31 <div class="col-md-8">
30 32 <div class="program-preview--box contraste-box">
31 33 <div class="program-preview--banner" ng-style="{'background-image':'url( {{::pagePrograma.banner.src}} )'}"></div>
... ... @@ -55,24 +57,52 @@
55 57 </div>
56 58 </div>
57 59 </div>
  60 + <!-- Preview > coluna da direita -->
58 61 <div class="col-md-4">
59 62 <div class="row">
60   - <div class="col-xs-12" ng-if="pagePrograma.proposalsTopRated && pagePrograma.proposalsTopRated.length > 0">
61   - <h3 class="color-theme-fg">Propostas mais votadas</h3>
62   - <proposal-carousel proposals="pagePrograma.proposalsTopRated"></proposal-carousel>
63   - </div>
64   - <div class="col-xs-12" ng-if="pagePrograma.randomProposal" ng-class="{'focused-proposal': !!pagePrograma.search.proposal_id}">
65   - <h3 class="color-theme-fg">Propostas nesse programa</h3>
66   - <proposal-box proposal="pagePrograma.randomProposal" topic="pagePrograma.article" category="pagePrograma.category" can-vote="true" focus="{{pagePrograma.search.proposal_id}}" ></proposal-box>
  63 +
  64 + <!-- Top Proposals -->
  65 + <div>
  66 + <!-- Loading Top Proposals -->
  67 + <div ng-if="pagePrograma.loadingTopProposals">
  68 + <div class="alert alert-info" role="alert">
  69 + Carregando propostas mais votadas...
  70 + </div>
  71 + </div>
  72 +
  73 + <!-- Top Proposals > Carousel -->
  74 + <div class="col-xs-12" ng-if="!pagePrograma.loadingTopProposals && pagePrograma.proposalsTopRated && pagePrograma.proposalsTopRated.length > 0">
  75 + <h3 class="color-theme-fg">Propostas mais votadas</h3>
  76 + <proposal-carousel proposals="pagePrograma.proposalsTopRated"></proposal-carousel>
  77 + </div>
67 78 </div>
68   - <div class="col-xs-12" ng-if="!pagePrograma.randomProposal && !(pagePrograma.proposalsTopRated && pagePrograma.proposalsTopRated.length > 0)">
69   - <h3>Programas sem propostas</h3>
70   - <p>
71   - Este programa ainda não possui nenhuma proposta.
72   - <div class="button--themed">
73   - <button type="button" class="btn btn-block" ng-click="pagePrograma.showProposalForm()">Faça uma proposta</button>
  79 +
  80 + <!-- Proposal Box -->
  81 + <div>
  82 + <div class="col-xs-12" ng-if="!pagePrograma.loadingProposalBox && pagePrograma.randomProposal" ng-class="{'focused-proposal': !!pagePrograma.search.proposal_id}">
  83 + <h3 class="color-theme-fg">Propostas nesse programa</h3>
  84 + <proposal-box proposal="pagePrograma.randomProposal" topic="pagePrograma.article" category="pagePrograma.category" can-vote="true" focus="{{pagePrograma.search.proposal_id}}" ></proposal-box>
  85 + </div>
  86 +
  87 + <!-- Loading Proposal Box -->
  88 + <div ng-if="pagePrograma.loadingProposalBox">
  89 + <div class="alert alert-info" role="alert">
  90 + Carregando propostas nesse programa...
74 91 </div>
75   - </p>
  92 + </div>
  93 + </div>
  94 +
  95 + <!-- No Proposals? okay! -->
  96 + <div ng-if="!pagePrograma.loadingTopProposals && !pagePrograma.loadingProposalBox">
  97 + <div class="col-xs-12" ng-if="!pagePrograma.randomProposal && !(pagePrograma.proposalsTopRated && pagePrograma.proposalsTopRated.length > 0)">
  98 + <h3>Programas sem propostas</h3>
  99 + <p>
  100 + Este programa ainda não possui nenhuma proposta.
  101 + <div class="button--themed">
  102 + <button type="button" class="btn btn-block" ng-click="pagePrograma.showProposalForm()">Faça uma proposta</button>
  103 + </div>
  104 + </p>
  105 + </div>
76 106 </div>
77 107 </div>
78 108 </div>
... ...