Commit 1730f37c3a80e6acc671eae18f8f7121b1359cae

Authored by Leonardo Merlin
2 parents 4ff60ea5 53382f37

Merge branch 'feature-respostas'

@@ -4,6 +4,7 @@ bower_components/ @@ -4,6 +4,7 @@ bower_components/
4 .idea/ 4 .idea/
5 .tmp/ 5 .tmp/
6 dist/ 6 dist/
  7 +coverage/
7 .editorconfig 8 .editorconfig
8 script-staging.sh 9 script-staging.sh
9 10
@@ -13,12 +13,12 @@ function listFiles() { @@ -13,12 +13,12 @@ function listFiles() {
13 }); 13 });
14 14
15 return wiredep(wiredepOptions).js 15 return wiredep(wiredepOptions).js
16 - .concat([  
17 - path.join(conf.paths.src, '/app/**/*.module.js'),  
18 - path.join(conf.paths.src, '/app/**/*.js'),  
19 - path.join(conf.paths.src, '/**/*.spec.js'),  
20 - path.join(conf.paths.src, '/**/*.mock.js'),  
21 - path.join(conf.paths.src, '/**/*.html') 16 + .concat([
  17 + path.join(conf.paths.src, '/app/**/*.module.js'),
  18 + path.join(conf.paths.src, '/app/**/*.js'),
  19 + path.join(conf.paths.src, '/**/*.spec.js'),
  20 + path.join(conf.paths.src, '/**/*.mock.js'),
  21 + path.join(conf.paths.src, '/**/*.html')
22 ]); 22 ]);
23 } 23 }
24 24
@@ -45,14 +45,26 @@ module.exports = function(config) { @@ -45,14 +45,26 @@ module.exports = function(config) {
45 browsers : ['PhantomJS'], 45 browsers : ['PhantomJS'],
46 46
47 plugins : [ 47 plugins : [
48 - 'karma-phantomjs-launcher',  
49 - 'karma-angular-filesort',  
50 - 'karma-jasmine',  
51 - 'karma-ng-html2js-preprocessor' 48 + 'karma-jasmine',
  49 + 'karma-coverage',
  50 + 'karma-angular-filesort',
  51 + 'karma-ng-html2js-preprocessor',
  52 + 'karma-phantomjs-launcher',
  53 + 'karma-chrome-launcher',
  54 + 'karma-firefox-launcher'
52 ], 55 ],
53 56
54 preprocessors: { 57 preprocessors: {
55 - 'src/**/*.html': ['ng-html2js'] 58 + 'src/**/*.html': ['ng-html2js'],
  59 + 'src/app/**/*.js': ['coverage']
  60 + },
  61 +
  62 + reporters: ['progress', 'coverage'],
  63 +
  64 + coverageReporter: {
  65 + type : 'lcov', // HTML + LCOV
  66 + // type : 'cobertura', // supported by jenkins
  67 + dir : 'coverage/'
56 } 68 }
57 }; 69 };
58 70
@@ -39,6 +39,9 @@ @@ -39,6 +39,9 @@
39 "jshint-stylish": "~2.0.0", 39 "jshint-stylish": "~2.0.0",
40 "karma": "~0.12.36", 40 "karma": "~0.12.36",
41 "karma-angular-filesort": "~0.1.0", 41 "karma-angular-filesort": "~0.1.0",
  42 + "karma-chrome-launcher": "^0.2.1",
  43 + "karma-coverage": "^0.5.3",
  44 + "karma-firefox-launcher": "^0.1.7",
42 "karma-jasmine": "~0.3.5", 45 "karma-jasmine": "~0.3.5",
43 "karma-ng-html2js-preprocessor": "~0.1.2", 46 "karma-ng-html2js-preprocessor": "~0.1.2",
44 "karma-phantomjs-launcher": "~0.2.0", 47 "karma-phantomjs-launcher": "~0.2.0",
src/app/components/app-navbar/app-navbar.html
@@ -32,6 +32,7 @@ @@ -32,6 +32,7 @@
32 <li ui-sref-active="active"><a ui-sref="propostas">Propostas</a></li> 32 <li ui-sref-active="active"><a ui-sref="propostas">Propostas</a></li>
33 <li ui-sref-active="active"><a ui-sref="ranking">Ranking</a></li> 33 <li ui-sref-active="active"><a ui-sref="ranking">Ranking</a></li>
34 <li ui-sref-active="active"><a ui-sref="duvidas">Dúvidas</a></li> 34 <li ui-sref-active="active"><a ui-sref="duvidas">Dúvidas</a></li>
  35 + <li ui-sref-active="active"><a ui-sref="respostas">Respostas</a></li>
35 <li role="separator" class="divider hidden-xs hidden-sm"><span>|</span></li> 36 <li role="separator" class="divider hidden-xs hidden-sm"><span>|</span></li>
36 <li class="dropdown" style="border: none;"> 37 <li class="dropdown" style="border: none;">
37 <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Compartilhar <span aria-hidden="true" class="icon icon-social-share-small"></span></a> 38 <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Compartilhar <span aria-hidden="true" class="icon icon-social-share-small"></span></a>
src/app/components/category-list/category-list.directive.js
@@ -27,9 +27,11 @@ @@ -27,9 +27,11 @@
27 CategoryListController.prototype.init = function() { 27 CategoryListController.prototype.init = function() {
28 var vm = this; 28 var vm = this;
29 29
  30 + // Default values
30 if(!vm.isCollapsed){ 31 if(!vm.isCollapsed){
31 vm.isCollapsed = false; 32 vm.isCollapsed = false;
32 } 33 }
  34 +
33 }; 35 };
34 36
35 CategoryListController.prototype._disableUnselect = function() { 37 CategoryListController.prototype._disableUnselect = function() {
src/app/components/category-list/category-list.html
1 <div class="category-list"> 1 <div class="category-list">
2 <nav class="navigation"> 2 <nav class="navigation">
3 <h3 class="category-list--title" ng-click="vm.toogleList()"> 3 <h3 class="category-list--title" ng-click="vm.toogleList()">
4 - <span class="hidden-xs"><b>Programas</b> por Tema</span>  
5 - <span class="visible-xs"><b>Filtrar</b> por Tema <span class="glyphicon glyphicon-filter pull-right"></span></span> 4 + <!-- <span class="hidden-xs"><b>Programas</b> por Tema</span> -->
  5 + <!-- <span class="visible-xs"><b>Filtrar</b> por Tema <span class="glyphicon glyphicon-filter pull-right"></span></span> -->
  6 + <b>Filtrar</b> por Tema <span class="glyphicon glyphicon-filter pull-right"></span>
6 </h3> 7 </h3>
7 8
8 <!-- <div class="list-group ng-hide" ng-show="!vm.isCollapsed" ng-class="vm.selectedCategory.slug"> --> 9 <!-- <div class="list-group ng-hide" ng-show="!vm.isCollapsed" ng-class="vm.selectedCategory.slug"> -->
src/app/components/proposal-box/proposal-box.directive.js
@@ -90,17 +90,6 @@ @@ -90,17 +90,6 @@
90 }, 10); 90 }, 10);
91 }; 91 };
92 92
93 - ProposalBoxController.prototype.showContent = function(slug) {  
94 - var vm = this;  
95 -  
96 - vm.$state.go('programa', {  
97 - slug: slug,  
98 - proposal_id: vm.proposal.id  
99 - }, {  
100 - location: true  
101 - });  
102 - };  
103 -  
104 ProposalBoxController.prototype.canVote = function() { 93 ProposalBoxController.prototype.canVote = function() {
105 var vm = this; 94 var vm = this;
106 95
src/app/components/proposal-box/proposal-box.html
@@ -145,16 +145,18 @@ @@ -145,16 +145,18 @@
145 <div class="proposal-box--content-inner">{{vm.proposal.abstract}}</div> 145 <div class="proposal-box--content-inner">{{vm.proposal.abstract}}</div>
146 </div> 146 </div>
147 <div ng-hide="vm.showVote" class="proposal-box--join"> 147 <div ng-hide="vm.showVote" class="proposal-box--join">
148 - <button 148 + <a
149 class="btn btn-link color-theme-common-fg" 149 class="btn btn-link color-theme-common-fg"
150 ng-if="vm.archived === false" 150 ng-if="vm.archived === false"
151 - ng-click="vm.showContent(vm.topic.slug)"  
152 - >Participe</button>  
153 - <button 151 + ui-sref="programa({slug: vm.topic.slug, proposal_id: vm.proposal.id})"
  152 + ui-sref-opts="{location: true}"
  153 + >Participe</a>
  154 + <a
154 class="btn btn-link color-theme-common-fg" 155 class="btn btn-link color-theme-common-fg"
155 ng-if="vm.archived === true" 156 ng-if="vm.archived === true"
156 - ng-click="vm.showContent(vm.topic.slug)"  
157 - >Ir para o programa</button> 157 + ui-sref="programa({slug: vm.topic.slug, proposal_id: vm.proposal.id})"
  158 + ui-sref-opts="{location: true}"
  159 + >Ir para o programa</a>
158 </div> 160 </div>
159 <div ng-show="vm.showVote" class="proposal-box--actions text-center"> 161 <div ng-show="vm.showVote" class="proposal-box--actions text-center">
160 <div class="row"> 162 <div class="row">
@@ -185,7 +187,7 @@ @@ -185,7 +187,7 @@
185 </div> 187 </div>
186 </div> 188 </div>
187 </div> 189 </div>
188 - <div class="proposal-box--bottom text-center"> 190 + <div class="proposal-box--bottom text-center" ng-class="{archived: vm.archived}">
189 <div class="proposal-box--share"> 191 <div class="proposal-box--share">
190 <span>COMPARTILHE ESTA <b>PROPOSTA</b></span> 192 <span>COMPARTILHE ESTA <b>PROPOSTA</b></span>
191 <div class="dropdown"> 193 <div class="dropdown">
@@ -198,11 +200,25 @@ @@ -198,11 +200,25 @@
198 class="dropdown-menu dropdown-menu-right"></social-share> 200 class="dropdown-menu dropdown-menu-right"></social-share>
199 </div> 201 </div>
200 </div> 202 </div>
201 - <div class="proposal-box--ranking">  
202 - <div class="proposal-box--ranking-inner">  
203 - <span class="icon icon-small icon-ranking" aria-hidden="true"></span>  
204 - <span>Colocação nos resultados:</span>  
205 - <span>{{vm.proposal.ranking_position}}º</span> 203 +
  204 + <div ng-if="vm.archived === true">
  205 + <div class="proposal-box--archived color-theme-bg-complementar-2">
  206 + <div class="proposal-box--archived-inner">
  207 + <a ui-sref="programa({slug: vm.topic.slug, proposal_id: vm.proposal.id})" ui-sref-opts="{location: true}">
  208 + Veja as respostas e compromissos assumidos
  209 + <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
  210 + </a>
  211 + </div>
  212 + </div>
  213 + </div>
  214 +
  215 + <div ng-if="vm.archived === false">
  216 + <div class="proposal-box--ranking">
  217 + <div class="proposal-box--ranking-inner">
  218 + <span class="icon icon-small icon-ranking" aria-hidden="true"></span>
  219 + <span>Colocação nos resultados:</span>
  220 + <span>{{vm.proposal.ranking_position}}º</span>
  221 + </div>
206 </div> 222 </div>
207 </div> 223 </div>
208 </div> 224 </div>
src/app/components/proposal-box/proposal-box.scss
@@ -5,6 +5,8 @@ @@ -5,6 +5,8 @@
5 margin-bottom: 20px; 5 margin-bottom: 20px;
6 position: relative; 6 position: relative;
7 7
  8 + .contraste & { background-color: #000 !important; outline: 1px solid #eee; }
  9 +
8 &.focus { 10 &.focus {
9 border: 7px solid #000; 11 border: 7px solid #000;
10 border-radius: 12px; 12 border-radius: 12px;
@@ -37,6 +39,12 @@ @@ -37,6 +39,12 @@
37 border-bottom-right-radius: 5px; 39 border-bottom-right-radius: 5px;
38 // overflow: hidden; 40 // overflow: hidden;
39 border-bottom: 5px solid #dadada; 41 border-bottom: 5px solid #dadada;
  42 +
  43 + @each $category, $color in $categories {
  44 + .#{$category} &.archived {
  45 + border-bottom-color: map-get($categories-complementary-2, $category);
  46 + }
  47 + }
40 } 48 }
41 49
42 &--theme { 50 &--theme {
@@ -54,12 +62,25 @@ @@ -54,12 +62,25 @@
54 &--share { 62 &--share {
55 padding: 15px 0; 63 padding: 15px 0;
56 background-color: #e8e8e8; 64 background-color: #e8e8e8;
  65 +
  66 + .contraste & { background-color: #000 !important; outline: 1px solid #eee; }
57 } 67 }
58 68
59 - &--ranking { 69 + &--ranking,
  70 + &--archived {
60 font-weight: bold; 71 font-weight: bold;
61 padding: 10px 0; 72 padding: 10px 0;
62 background-color: #dadada; 73 background-color: #dadada;
  74 +
  75 + .contraste & { background-color: #111 !important; outline: 1px solid #eee; }
  76 + }
  77 +
  78 + &--archived {
  79 + text-transform: uppercase;
  80 + font-size: 12px;
  81 + line-height: 23px;
  82 +
  83 + a { color: #fff; }
63 } 84 }
64 85
65 .action-label { 86 .action-label {
src/app/index.route.js
@@ -148,6 +148,18 @@ @@ -148,6 +148,18 @@
148 } 148 }
149 } 149 }
150 }) 150 })
  151 + .state('respostas', {
  152 + url: '/respostas?tema&filtro',
  153 + reloadOnSearch: false,
  154 + ncyBreadcrumb: {label: 'Respostas'},
  155 + views: {
  156 + 'main': {
  157 + templateUrl: 'app/pages/respostas/respostas.html',
  158 + controller: 'RespostasPageController',
  159 + controllerAs: 'pageRespostas'
  160 + }
  161 + }
  162 + })
151 .state('sobre', { 163 .state('sobre', {
152 url: '/sobre', 164 url: '/sobre',
153 ncyBreadcrumb: {label: 'Sobre'}, 165 ncyBreadcrumb: {label: 'Sobre'},
src/app/index.scss
@@ -75,13 +75,14 @@ $darken: 15%; @@ -75,13 +75,14 @@ $darken: 15%;
75 body { 75 body {
76 font-family: "Open Sans", sans-serif; 76 font-family: "Open Sans", sans-serif;
77 &.contraste { 77 &.contraste {
78 - color: #fff;  
79 - background-color: #000; 78 + color: #fff !important;
  79 + background-color: #000 !important;
80 80
81 a, 81 a,
82 .btn { 82 .btn {
83 - color: #fff;  
84 - background-color: #000; 83 + color: #ff0 !important;
  84 + background-color: #000 !important;
  85 + text-decoration: underline !important;
85 } 86 }
86 } 87 }
87 } 88 }
@@ -282,6 +283,8 @@ body { @@ -282,6 +283,8 @@ body {
282 283
283 .contraste & .color-theme-fg { color: #fff; } 284 .contraste & .color-theme-fg { color: #fff; }
284 .contraste & .color-theme-bg { background-color: #000;} 285 .contraste & .color-theme-bg { background-color: #000;}
  286 + .contraste & .color-theme-bg-complementar-1 { background-color: #111; outline: 1px solid #eee;}
  287 + .contraste & .color-theme-bg-complementar-2 { background-color: #222; outline: 1px solid #ddd;}
285 } 288 }
286 } 289 }
287 290
@@ -289,7 +292,9 @@ $common-color: #5E739E; @@ -289,7 +292,9 @@ $common-color: #5E739E;
289 .color-theme-common-fg {color: $common-color; } 292 .color-theme-common-fg {color: $common-color; }
290 .color-theme-common-bg {background-color: $common-color; } 293 .color-theme-common-bg {background-color: $common-color; }
291 .color-fg-white {color: #fff; } 294 .color-fg-white {color: #fff; }
292 -.color-bg-gray {color: #ccc; } 295 +.color-bg-white {background-color: #fff; }
  296 +.color-fg-gray {color: #ccc; }
  297 +.color-bg-gray {background-color: #ccc; }
293 298
294 // Contraste 299 // Contraste
295 .contraste { 300 .contraste {
src/app/pages/programas/programa.controller.js
@@ -275,4 +275,20 @@ @@ -275,4 +275,20 @@
275 275
276 vm.proposalStatus = null; 276 vm.proposalStatus = null;
277 }; 277 };
  278 +
  279 + ProgramaPageController.prototype.toggleContentVisibility = function() {
  280 + var $sectionContent = angular.element('.section-content');
  281 +
  282 + if(!$sectionContent || $sectionContent.length === 0){
  283 + vm.$log.warn('".section-content" not found.');
  284 + return;
  285 + }
  286 +
  287 + if($sectionContent.is(':visible')){
  288 + $sectionContent.slideUp();
  289 + }else{
  290 + $sectionContent.slideDown();
  291 + angular.element('html,body').animate({scrollTop: $sectionContent.offset().top}, 'fast');
  292 + }
  293 + };
278 })(); 294 })();
src/app/pages/programas/programa.html
1 <div class="container"> 1 <div class="container">
2 - <div class="row">  
3 - <div class="col-sm-12">  
4 - <div ncy-breadcrumb></div> 2 + <div class="row">
  3 + <div class="col-sm-12">
  4 + <div ncy-breadcrumb></div>
  5 + </div>
5 </div> 6 </div>
6 - </div>  
7 </div> 7 </div>
8 -  
9 <div class="page--program"> 8 <div class="page--program">
10 - <section>  
11 - <div class="container">  
12 - <div class="row">  
13 - <div class="col-sm-12">  
14 - <div ng-if="!pagePrograma.article && pagePrograma.loading" class="alert alert-info" role="alert">Carregando detalhes sobre o progama...</div>  
15 - <div ng-if="!pagePrograma.article && pagePrograma.error" class="alert alert-warning" role="alert">Erro ao carregar o programa.</div> 9 + <section ng-if="!pagePrograma.article">
  10 + <div class="container">
  11 + <div class="row">
  12 + <div class="col-sm-12">
  13 + <div ng-if="pagePrograma.loading" class="alert alert-info" role="alert">Carregando detalhes sobre o progama...</div>
  14 + <div ng-if="pagePrograma.error" class="alert alert-warning" role="alert">Erro ao carregar o programa.</div>
  15 + </div>
  16 + </div>
16 </div> 17 </div>
17 - </div>  
18 - </div>  
19 - </section>  
20 -  
21 - <div role="main" ng-class="pagePrograma.category.slug">  
22 - <section ng-if="pagePrograma.article.body">  
23 - <div class="container">  
24 - <div class="row">  
25 - <article class="program-preview">  
26 - <!-- Preview > Titulo -->  
27 - <div class="col-md-12">  
28 - <h1 class="program-preview--title color-theme-fg">{{::pagePrograma.article.title}}</h1> 18 + </section>
  19 + <div role="main" ng-class="pagePrograma.category.slug">
  20 + <section ng-if="pagePrograma.article.body">
  21 + <div class="container">
  22 + <div class="row">
  23 + <article class="program-preview" ng-class="{ 'program-preview--archived': pagePrograma.article.archived }">
  24 + <!-- Preview > Titulo -->
  25 + <div class="col-md-12">
  26 + <h1 class="program-preview--title color-theme-fg">{{::pagePrograma.article.title}}</h1>
  27 + </div>
  28 + <!-- Preview > coluna da esquerda -->
  29 + <div class="col-md-8">
  30 + <div class="program-preview--box contraste-box">
  31 + <div class="program-preview--banner" ng-style="{'background-image':'url( {{::pagePrograma.banner.src}} )'}"></div>
  32 + <div class="program-preview--box--content-wrapper">
  33 + <div class="program-preview--icon icon-wrapper-rounded color-theme-bg" ng-class="pagePrograma.category.slug">
  34 + <span class="icon" ng-class="'icon-tema-' + pagePrograma.category.slug"></span>
  35 + </div>
  36 + <div class="program-preview--abstract color-theme-fg" ng-if="pagePrograma.article.archived === false">
  37 + <h2>{{::stripHtml(pagePrograma.article.summary)}}</h2>
  38 + </div>
  39 + <div class="program-preview--abstract-details" ng-if="pagePrograma.article.archived === false">
  40 + <div ng-bind-html="pagePrograma.article.summaryExtended"></div>
  41 + </div>
  42 + <div class="program-preview--share">
  43 + <div class="program-preview--share-label">COMPARTILHE ESTE <b>PROGRAMA</b></div>
  44 + <div class="dropdown">
  45 + <button id="dropdown-share-btn" class="btn btn-link dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Mostrar ou esconder a lista redes sociais para compartilhamento"><span class="icon icon-social-share-small" aria-hidden="true"></span></button>
  46 + <social-share social-url="pagePrograma.$state.href('programa', {slug: pagePrograma.article.slug}, {absolute: true})" social-text="pagePrograma.article.title" social-image="pagePrograma.banner.src" arrow-class="social-share--arrow" class="dropdown-menu dropdown-menu-right"></social-share>
  47 + </div>
  48 + </div>
  49 + <div class="program-preview--make-proposal" ng-if="pagePrograma.article.archived === false">
  50 + <div class="row">
  51 + <div class="col-sm-6">
  52 + <div class="button--themed">
  53 + <button type="button" class="btn btn-block" ng-click="pagePrograma.showProposalForm()">Faça uma proposta</button>
  54 + </div>
  55 + </div>
  56 + </div>
  57 + </div>
  58 + </div>
  59 + </div>
  60 + </div>
  61 + <!-- Preview > coluna da direita -->
  62 + <div class="col-md-4">
  63 + <div class="row">
  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 + <!-- Top Proposals > Carousel -->
  73 + <div class="col-xs-12" ng-if="!pagePrograma.loadingTopProposals && pagePrograma.proposalsTopRated && pagePrograma.proposalsTopRated.length > 0">
  74 + <h3 class="color-theme-fg">Propostas mais votadas</h3>
  75 + <proposal-carousel proposals="pagePrograma.proposalsTopRated" archived="pagePrograma.article.archived"></proposal-carousel>
  76 + </div>
  77 + </div>
  78 + <!-- Proposal Box -->
  79 + <div ng-if="!pagePrograma.article.archived">
  80 + <div class="col-xs-12" ng-if="!pagePrograma.loadingProposalBox && pagePrograma.randomProposal" ng-class="{'focused-proposal': !!pagePrograma.search.proposal_id}">
  81 + <h3 class="color-theme-fg">Apoie outras propostas</h3>
  82 + <proposal-box proposal="pagePrograma.randomProposal" topic="pagePrograma.article" category="pagePrograma.category" show-vote="true" focus="{{pagePrograma.search.proposal_id}}" do-vote="pagePrograma.vote(proposal_id, value)" archived="pagePrograma.article.archived"></proposal-box>
  83 + </div>
  84 + <!-- Loading Proposal Box -->
  85 + <div ng-if="pagePrograma.loadingProposalBox">
  86 + <div class="alert alert-info" role="alert">
  87 + Carregando propostas nesse programa...
  88 + </div>
  89 + </div>
  90 + </div>
  91 + <!-- No Proposals? okay! -->
  92 + <div ng-if="!pagePrograma.loadingTopProposals && !pagePrograma.loadingProposalBox">
  93 + <div class="col-xs-12" ng-if="!pagePrograma.randomProposal && !(pagePrograma.proposalsTopRated && pagePrograma.proposalsTopRated.length > 0)">
  94 + <h3>Programas sem propostas</h3>
  95 + <p>
  96 + Este programa ainda não possui nenhuma proposta.
  97 + <div class="button--themed">
  98 + <button type="button" class="btn btn-block" ng-click="pagePrograma.showProposalForm()">Faça uma proposta</button>
  99 + </div>
  100 + </p>
  101 + </div>
  102 + </div>
  103 + </div>
  104 + </div>
  105 + </article>
  106 + </div>
29 </div> 107 </div>
30 - <!-- Preview > coluna da esquerda -->  
31 - <div class="col-md-8">  
32 - <div class="program-preview--box contraste-box">  
33 - <div class="program-preview--banner" ng-style="{'background-image':'url( {{::pagePrograma.banner.src}} )'}"></div>  
34 - <div class="program-preview--box--content-wrapper">  
35 - <div class="program-preview--icon icon-wrapper-rounded color-theme-bg" ng-class="pagePrograma.category.slug">  
36 - <span class="icon" ng-class="'icon-tema-' + pagePrograma.category.slug"></span>  
37 - </div>  
38 - <div class="program-preview--abstract color-theme-fg">  
39 - <h2>{{::stripHtml(pagePrograma.article.summary)}}</h2>  
40 - </div>  
41 - <div class="program-preview--abstract-details">  
42 - <div ng-bind-html="pagePrograma.article.summaryExtended"></div>  
43 - </div>  
44 - <div class="program-preview--share">  
45 - <div class="program-preview--share-label">COMPARTILHE ESTE <b>PROGRAMA</b></div>  
46 - <div class="dropdown">  
47 - <button id="dropdown-share-btn" class="btn btn-link dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Mostrar ou esconder a lista redes sociais para compartilhamento"><span class="icon icon-social-share-small" aria-hidden="true"></span></button>  
48 - <social-share  
49 - social-url="pagePrograma.$state.href('programa', {slug: pagePrograma.article.slug}, {absolute: true})"  
50 - social-text="pagePrograma.article.title"  
51 - social-image="pagePrograma.banner.src"  
52 - arrow-class="social-share--arrow"  
53 - class="dropdown-menu dropdown-menu-right"  
54 - ></social-share> 108 + </section>
  109 + <section id="section-proposal-list" class="proposal-extended-section" style="display:none;" ng-if="pagePrograma.proposalsTopFive && pagePrograma.proposalsTopFive.length > 0">
  110 + <div class="container">
  111 + <div class="proposal-extended-section-header">
  112 + <h3 class="color-theme-fg">Resultados de propostas mais votadas</h3>
  113 + <button type="button" aria-label="Close" class="btn btn-close" ng-click="pagePrograma.hideProposalsList()">
  114 + <span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
  115 + </button>
  116 + </div>
  117 + <div class="proposal-extended-section-content">
  118 + <proposal-list proposals="pagePrograma.proposalsTopFive" archived="pagePrograma.article.archived"></proposal-list>
  119 + <div class="row text-center">
  120 + <div class="col-xs-12">
  121 + <a ui-sref="ranking({tema: pagePrograma.category.slug, programa: pagePrograma.article.slug})" class="btn btn-link">
  122 + <span ng-if="pagePrograma.total_proposals > 5">Veja todas as {{pagePrograma.total_proposals}} propostas</span>
  123 + </a>
  124 + </div>
55 </div> 125 </div>
56 - </div>  
57 - <div class="program-preview--make-proposal"> 126 + </div>
  127 + </div>
  128 + </section>
  129 + <section id="section-proposal-form" class="proposal-extended-section" style="display:none;">
  130 + <div class="container">
  131 + <div class="proposal-extended-section-header">
  132 + <button type="button" aria-label="Close" class="btn btn-close" ng-click="pagePrograma.hideProposalForm()">
  133 + <span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
  134 + </button>
  135 + </div>
  136 + <div ng-if="!pagePrograma.$rootScope.currentUser">
  137 + <show-message type="'alert'" message="'Você não está logado!'" description="'Você precisa estar logado para enviar uma proposta.'"></show-message>
58 <div class="row"> 138 <div class="row">
59 - <div class="col-sm-6">  
60 - <div class="button--themed" ng-if="!pagePrograma.article.archived">  
61 - <button type="button" class="btn btn-block" ng-click="pagePrograma.showProposalForm()">Faça uma proposta</button> 139 + <div class="col-sm-4"></div>
  140 + <div class="col-sm-8 text-center-sm">
  141 + <a ui-sref="entrar({redirect_uri: pagePrograma.sendProposalRedirectURI})">Clique aqui para ir para a página de login</a>
62 </div> 142 </div>
63 - <div ng-if="pagePrograma.article.archived">  
64 - <button type="button"  
65 - class="btn btn-block disabled"  
66 - title="Este programa já foi analisado. Não está mais habilitado a receber novas propostas."  
67 - ng-click="pagePrograma.showDetailAboutArchived = true"  
68 - >Faça uma proposta</button> 143 + </div>
  144 + </div>
  145 + <div ng-if="pagePrograma.$rootScope.currentUser">
  146 + <div class="proposal-extended-section-content">
  147 + <div ng-if="pagePrograma.proposalStatus === pagePrograma.PROPOSAL_STATUS.SUCCESS">
  148 + <show-message type="'success'" message="'Proposta enviada com sucesso!'" description="'Sua proposta foi enviada para a nossa equipe de moderação. Em aproximadamente 72 horas você receberá em seu endereço de e-mail uma resposta sobre a sua proposta.'"></show-message>
  149 + <div class="row">
  150 + <div class="col-sm-4"></div>
  151 + <div class="col-sm-8 text-center-sm">
  152 + <button type="button" class="btn btn-link" ng-click="pagePrograma.sendAnotherProposal()">
  153 + Clique aqui para enviar outra proposta
  154 + </button>
  155 + </div>
  156 + </div>
  157 + </div>
  158 + <div ng-if="pagePrograma.proposalStatus === pagePrograma.PROPOSAL_STATUS.SENDING">
  159 + Enviando...
  160 + </div>
  161 + <div ng-if="pagePrograma.proposalStatus === pagePrograma.PROPOSAL_STATUS.ERROR">
  162 + <show-message type="'error'" message="'Erro ' + pagePrograma.error.code + '!'" description="pagePrograma.error.message"></show-message>
  163 + <div class="row">
  164 + <div class="col-sm-4"></div>
  165 + <div class="col-sm-8 text-center-sm">
  166 + <button type="button" class="btn btn-link" ng-click="pagePrograma.sendAnotherProposal()">
  167 + Clique aqui para enviar outra proposta
  168 + </button>
  169 + </div>
  170 + </div>
  171 + </div>
  172 + <div ng-if="!pagePrograma.proposalStatus">
  173 + <cadastro-proposta program="pagePrograma.article" status="pagePrograma.proposalStatus"></cadastro-proposta>
69 </div> 174 </div>
70 - </div>  
71 - <div class="col-sm-6" ng-show="pagePrograma.showDetailAboutArchived === true">  
72 - <p>(&#9679;) Este programa está arquivado.<br>Não pode receber novas propostas.</p>  
73 - </div>  
74 </div> 175 </div>
75 - </div>  
76 </div> 176 </div>
77 - </div>  
78 </div> 177 </div>
79 - <!-- Preview > coluna da direita -->  
80 - <div class="col-md-4">  
81 - <div class="row">  
82 -  
83 - <!-- Top Proposals -->  
84 - <div>  
85 - <!-- Loading Top Proposals -->  
86 - <div ng-if="pagePrograma.loadingTopProposals">  
87 - <div class="alert alert-info" role="alert">  
88 - Carregando propostas mais votadas... 178 + </section>
  179 + <section id="section-archived-banner" class="color-theme-bg" ng-if="pagePrograma.article.archived">
  180 + <div class="container">
  181 + <div class="row">
  182 + <div class="col-sm-12">
  183 + <h2>
  184 + <div class="program-banner--icon-container" aria-hidden="true">
  185 + <div class="program-banner--icon icon-wrapper-rounded color-bg-white" ng-class="pagePrograma.category.slug">
  186 + <span class="icon" ng-class="'icon-discussion'"></span>
  187 + </div>
  188 + </div>
  189 + <span class="archived-banner--title1">As propostas para este programa já foram</span>
  190 + <br/>
  191 + <span class="archived-banner--title2">respondidas pelo governo! Confira as respostas:</span>
  192 + </h2>
89 </div> 193 </div>
90 - </div>  
91 -  
92 - <!-- Top Proposals > Carousel -->  
93 - <div class="col-xs-12" ng-if="!pagePrograma.loadingTopProposals && pagePrograma.proposalsTopRated && pagePrograma.proposalsTopRated.length > 0">  
94 - <h3 class="color-theme-fg">Propostas mais votadas</h3>  
95 - <proposal-carousel  
96 - proposals="pagePrograma.proposalsTopRated"  
97 - archived="pagePrograma.article.archived"  
98 - ></proposal-carousel>  
99 - </div>  
100 </div> 194 </div>
101 -  
102 - <!-- Proposal Box -->  
103 - <div ng-if="!pagePrograma.article.archived">  
104 - <div class="col-xs-12" ng-if="!pagePrograma.loadingProposalBox && pagePrograma.randomProposal" ng-class="{'focused-proposal': !!pagePrograma.search.proposal_id}">  
105 - <h3 class="color-theme-fg">Apoie outras propostas</h3>  
106 - <proposal-box  
107 - proposal="pagePrograma.randomProposal"  
108 - topic="pagePrograma.article"  
109 - category="pagePrograma.category"  
110 - show-vote="true"  
111 - focus="{{pagePrograma.search.proposal_id}}"  
112 - do-vote="pagePrograma.vote(proposal_id, value)"  
113 - archived="pagePrograma.article.archived"  
114 - ></proposal-box>  
115 - </div>  
116 -  
117 - <!-- Loading Proposal Box -->  
118 - <div ng-if="pagePrograma.loadingProposalBox">  
119 - <div class="alert alert-info" role="alert">  
120 - Carregando propostas nesse programa... 195 + </div>
  196 + </section>
  197 + <section id="section-response" ng-if="pagePrograma.article.archived">
  198 + <div ng-if="!pagePrograma.proposalsTopRated">
  199 + <div class="container">
  200 + <div class="row">
  201 + <div class="col-sm-12">
  202 + <div class="alert alert-info" role="alert">
  203 + Carregando propostas mais votadas...
  204 + </div>
  205 + </div>
121 </div> 206 </div>
122 - </div>  
123 - </div>  
124 -  
125 - <!-- No Proposals? okay! -->  
126 - <div ng-if="!pagePrograma.loadingTopProposals && !pagePrograma.loadingProposalBox">  
127 - <div class="col-xs-12" ng-if="!pagePrograma.randomProposal && !(pagePrograma.proposalsTopRated && pagePrograma.proposalsTopRated.length > 0)">  
128 - <h3>Programas sem propostas</h3>  
129 - <p>  
130 - Este programa ainda não possui nenhuma proposta.  
131 - <div class="button--themed">  
132 - <button type="button" class="btn btn-block" ng-click="pagePrograma.showProposalForm()">Faça uma proposta</button>  
133 - </div>  
134 - </p>  
135 - </div>  
136 </div> 207 </div>
137 - </div>  
138 - </div>  
139 - </article>  
140 - </div>  
141 - </div>  
142 - </section>  
143 -  
144 - <section id="section-proposal-list" class="proposal-extended-section" style="display:none;" ng-if="pagePrograma.proposalsTopFive && pagePrograma.proposalsTopFive.length > 0">  
145 - <div class="container">  
146 - <div class="proposal-extended-section-header">  
147 - <h3 class="color-theme-fg">Resultados de propostas mais votadas</h3>  
148 - <button type="button" aria-label="Close" class="btn btn-close" ng-click="pagePrograma.hideProposalsList()">  
149 - <span class="glyphicon glyphicon-remove" aria-hidden="true"></span>  
150 - </button>  
151 - </div>  
152 - <div class="proposal-extended-section-content">  
153 - <proposal-list proposals="pagePrograma.proposalsTopFive" archived="pagePrograma.article.archived"></proposal-list>  
154 - <div class="row text-center">  
155 - <div class="col-xs-12">  
156 - <a ui-sref="ranking({tema: pagePrograma.category.slug, programa: pagePrograma.article.slug})" class="btn btn-link">  
157 - <span ng-if="pagePrograma.total_proposals > 5">Veja todas as {{pagePrograma.total_proposals}} propostas</span>  
158 - </a>  
159 </div> 208 </div>
160 - </div>  
161 - </div>  
162 - </div>  
163 - </section>  
164 -  
165 - <section id="section-proposal-form" class="proposal-extended-section" style="display:none;">  
166 - <div class="container">  
167 - <div class="proposal-extended-section-header">  
168 - <button type="button" aria-label="Close" class="btn btn-close" ng-click="pagePrograma.hideProposalForm()">  
169 - <span class="glyphicon glyphicon-remove" aria-hidden="true"></span>  
170 - </button>  
171 - </div>  
172 - <div ng-if="!pagePrograma.$rootScope.currentUser">  
173 - <show-message  
174 - type="'alert'"  
175 - message="'Você não está logado!'"  
176 - description="'Você precisa estar logado para enviar uma proposta.'"  
177 - ></show-message>  
178 - <div class="row">  
179 - <div class="col-sm-4"></div>  
180 - <div class="col-sm-8 text-center-sm">  
181 - <a ui-sref="entrar({redirect_uri: pagePrograma.sendProposalRedirectURI})">Clique aqui para ir para a página de login</a>  
182 - </div>  
183 - </div>  
184 - </div>  
185 - <div ng-if="pagePrograma.$rootScope.currentUser">  
186 - <div class="proposal-extended-section-content">  
187 - <div ng-if="pagePrograma.proposalStatus === pagePrograma.PROPOSAL_STATUS.SUCCESS">  
188 - <show-message  
189 - type="'success'"  
190 - message="'Proposta enviada com sucesso!'"  
191 - description="'Sua proposta foi enviada para a nossa equipe de moderação. Em aproximadamente 72 horas você receberá em seu endereço de e-mail uma resposta sobre a sua proposta.'"  
192 - ></show-message>  
193 - <div class="row">  
194 - <div class="col-sm-4"></div>  
195 - <div class="col-sm-8 text-center-sm">  
196 - <button type="button" class="btn btn-link" ng-click="pagePrograma.sendAnotherProposal()">  
197 - Clique aqui para enviar outra proposta  
198 - </button> 209 + <div ng-if="pagePrograma.proposalsTopRated">
  210 + <div class="sub-section" ng-repeat="proposal in pagePrograma.proposalsTopRated">
  211 + <div class="container">
  212 + <div class="row">
  213 + <div class="col-sm-12">
  214 + <h3 class="color-theme-fg">{{($index+1)}}a proposta mais votada:</h3>
  215 + <p>{{::proposal.abstract}}</p>
  216 +
  217 + <div class="button--themed">
  218 + <button class="btn btn-block">
  219 + Veja a resposta e o compromisso do governo
  220 + <div class="button-left-icon">
  221 + <span class="glyphicon glyphicon-chevron-down pull-right color-theme-common-fg" aria-hidden="true"></span>
  222 + </div>
  223 + </button>
  224 + </div>
  225 + </div>
  226 + </div>
  227 + </div>
199 </div> 228 </div>
200 - </div>  
201 - </div>  
202 - <div ng-if="pagePrograma.proposalStatus === pagePrograma.PROPOSAL_STATUS.SENDING">  
203 - Enviando...  
204 </div> 229 </div>
205 - <div ng-if="pagePrograma.proposalStatus === pagePrograma.PROPOSAL_STATUS.ERROR">  
206 - <show-message  
207 - type="'error'"  
208 - message="'Erro ' + pagePrograma.error.code + '!'"  
209 - description="pagePrograma.error.message"  
210 - ></show-message>  
211 - <div class="row">  
212 - <div class="col-sm-4"></div>  
213 - <div class="col-sm-8 text-center-sm">  
214 - <button type="button" class="btn btn-link" ng-click="pagePrograma.sendAnotherProposal()">  
215 - Clique aqui para enviar outra proposta  
216 - </button> 230 + <div class="response--context vertical-padding">
  231 + <div class="container">
  232 + <div class="row">
  233 + <div class="col-sm-5">
  234 + <div class="button--themed">
  235 + <a class="btn btn-block" ui-sref="respostas">
  236 + <span>Veja todas as propostas</span>
  237 + <br/>
  238 + <span>Respondidas pelo governo</span>
  239 + <span class="glyphicon glyphicon-chevron-right"></span>
  240 + </a>
  241 + </div>
  242 + </div>
  243 + <div class="col-sm-2"></div>
  244 + <div class="col-sm-5">
  245 + <div class="button--themed">
  246 + <button type="button" class="btn btn-block" ng-click="pagePrograma.toggleContentVisibility()">
  247 + <span>Informações sobre o programa</span>
  248 + <br/>
  249 + <span>"{{::pagePrograma.article.title}}"</span>
  250 + <span class="glyphicon glyphicon-chevron-right"></span>
  251 + </button>
  252 + </div>
  253 + </div>
  254 + </div>
217 </div> 255 </div>
218 - </div>  
219 </div> 256 </div>
220 - <div ng-if="!pagePrograma.proposalStatus">  
221 - <cadastro-proposta program="pagePrograma.article" status="pagePrograma.proposalStatus"></cadastro-proposta>  
222 - </div>  
223 - </div>  
224 - </div>  
225 - </div>  
226 - </section>  
227 -  
228 - <section class="section-content">  
229 - <article class="program-content" ng-if="pagePrograma.article" ng-class="pagePrograma.article.slug">  
230 - <div ng-bind-html="pagePrograma.article.bodyTrusted"></div>  
231 - </article>  
232 - </section>  
233 - </div> 257 + </section>
  258 + <section class="section-content" ng-style="{ display: pagePrograma.article.archived ? 'none': 'block' }">
  259 + <article class="program-content" ng-if="pagePrograma.article" ng-class="pagePrograma.article.slug">
  260 + <div ng-bind-html="pagePrograma.article.bodyTrusted"></div>
  261 + </article>
  262 + </section>
  263 + </div>
234 </div> 264 </div>
src/app/pages/programas/programas.scss
@@ -182,5 +182,91 @@ @@ -182,5 +182,91 @@
182 .proposal-extended-section-header { 182 .proposal-extended-section-header {
183 position: relative; 183 position: relative;
184 } 184 }
  185 +
  186 + #section-archived-banner {
  187 + position: relative;
  188 + padding: 20px 0;
  189 + color: #fff;
  190 +
  191 + margin-top: 30px;
  192 +
  193 + h2 {
  194 + font-size: 28px;
  195 + line-height: 40px;
  196 +
  197 + @media screen and (max-width: $screen-md) {
  198 + font-size: 22px;
  199 + line-height: 30px;
  200 + }
  201 +
  202 + @media screen and (max-width: $screen-xs) {
  203 + font-size: 18px;
  204 + line-height: 24px;
  205 + }
  206 + }
  207 +
  208 + .program-banner--icon {
  209 + float: left;
  210 + margin-right: 20px;
  211 + width: 85px;
  212 + height: 85px;
  213 + overflow: hidden;
  214 +
  215 + .icon-discussion {
  216 + position: relative;
  217 + top: -3px;
  218 + left: -3px;
  219 + display: block;
  220 + width: 85px;
  221 + height: 85px;
  222 + background: red;
  223 + }
  224 + }
  225 +
  226 + .archived-banner--title2 {
  227 + font-weight: bold;
  228 + }
  229 + }
  230 +
  231 + #section-response {
  232 +
  233 + h3 {
  234 + font-weight: bold;
  235 + }
  236 + .sub-section{
  237 + padding: 20px 0;
  238 + &:nth-child(odd) {
  239 + background-color: #f1f1f1;
  240 + }
  241 +
  242 + &:nth-child(even) {
  243 + background-color: #ffffff;
  244 + }
  245 + }
  246 +
  247 + .button--themed {
  248 + position: relative;
  249 + max-width: 500px;
  250 +
  251 + button {
  252 + text-transform: uppercase;
  253 + letter-spacing: 1px;
  254 + }
  255 + }
  256 +
  257 + .button-left-icon {
  258 + position: absolute;
  259 + top: 15px;
  260 + right: 15px;
  261 +
  262 + .glyphicon {
  263 + position: relative;
  264 + top: -2px;
  265 + background-color: #fff;
  266 + padding: 6px 5px 5px 6px;
  267 + border-radius: 100%;
  268 + }
  269 + }
  270 + }
185 } 271 }
186 272
src/app/pages/propostas/propostas.controller.js
@@ -18,7 +18,7 @@ @@ -18,7 +18,7 @@
18 18
19 vm.init(); 19 vm.init();
20 vm.loadData(); 20 vm.loadData();
21 - // vm.attachListeners(); // attach listeners after load data (SYNC) 21 + vm.attachListeners();
22 vm.$rootScope.focusMainContent(); 22 vm.$rootScope.focusMainContent();
23 23
24 $log.debug('PropostasPageController'); 24 $log.debug('PropostasPageController');
@@ -31,11 +31,22 @@ @@ -31,11 +31,22 @@
31 vm.per_page = 10; 31 vm.per_page = 10;
32 vm.themes = null; 32 vm.themes = null;
33 vm.selectedTheme = null; 33 vm.selectedTheme = null;
34 - vm.filtredPrograms = null;  
35 vm.filtredProposals = null; 34 vm.filtredProposals = null;
36 vm.query = null; 35 vm.query = null;
37 vm.search = vm.$location.search(); 36 vm.search = vm.$location.search();
38 37
  38 + if (vm.search.tema) {
  39 + vm._filtredByThemeSlug = vm.search.tema;
  40 + }
  41 +
  42 + if (vm.search.filtro) {
  43 + vm._filtredByQuery = vm.search.filtro;
  44 + }
  45 +
  46 + if (vm.search.tema || vm.search.filtro) {
  47 + vm.loadingFilter = true;
  48 + }
  49 +
39 vm.loading = null; 50 vm.loading = null;
40 vm.error = null; 51 vm.error = null;
41 }; 52 };
@@ -56,12 +67,8 @@ @@ -56,12 +67,8 @@
56 vm.themes = themes; 67 vm.themes = themes;
57 vm.loadingThemes = false; 68 vm.loadingThemes = false;
58 vm.loading = false; 69 vm.loading = false;
59 -  
60 - // REMOVED: function called twice.  
61 - // vm.loadProposals(function() {  
62 - // vm.attachListeners();  
63 - // });  
64 - vm.attachListeners(); 70 +
  71 + vm.filter();
65 }, function(error) { 72 }, function(error) {
66 vm.error = error; 73 vm.error = error;
67 vm.$log.error(error); 74 vm.$log.error(error);
@@ -70,30 +77,6 @@ @@ -70,30 +77,6 @@
70 }); 77 });
71 }; 78 };
72 79
73 - // PropostasPageController.prototype.loadProposals = function(cb) {  
74 - // var vm = this;  
75 -  
76 - // // load Proposals  
77 - // vm.loadingProposals = true;  
78 - // vm.DialogaService.searchProposals({  
79 - // page: vm.page,  
80 - // per_page: vm.per_page  
81 - // }, function(data) {  
82 - // vm.filtredProposals = data.articles;  
83 - // vm.total_proposals = parseInt(data._obj.headers('total'));  
84 -  
85 - // vm.loadingProposals = false;  
86 -  
87 - // if (cb) {  
88 - // cb();  
89 - // }  
90 - // }, function(error) {  
91 - // vm.error = error;  
92 - // vm.$log.error(error);  
93 - // vm.loadingProposals = false;  
94 - // });  
95 - // };  
96 -  
97 PropostasPageController.prototype.attachListeners = function() { 80 PropostasPageController.prototype.attachListeners = function() {
98 var vm = this; 81 var vm = this;
99 82
@@ -104,13 +87,19 @@ @@ -104,13 +87,19 @@
104 vm.$scope.$watch('pagePropostas.selectedTheme', function(newValue/*, oldValue*/) { 87 vm.$scope.$watch('pagePropostas.selectedTheme', function(newValue/*, oldValue*/) {
105 vm.search.tema = newValue ? newValue.slug : null; 88 vm.search.tema = newValue ? newValue.slug : null;
106 vm.$location.search('tema', vm.search.tema); 89 vm.$location.search('tema', vm.search.tema);
107 - vm.filterProposals(); 90 +
  91 + if (!vm.loadingFilter) {
  92 + vm.filterProposals();
  93 + }
108 }); 94 });
109 95
110 vm.$scope.$watch('pagePropostas.query', function(newValue/*, oldValue*/) { 96 vm.$scope.$watch('pagePropostas.query', function(newValue/*, oldValue*/) {
111 vm.search.filtro = newValue ? newValue : null; 97 vm.search.filtro = newValue ? newValue : null;
112 vm.$location.search('filtro', vm.search.filtro); 98 vm.$location.search('filtro', vm.search.filtro);
113 - vm.filterProposals(); 99 +
  100 + if (!vm.loadingFilter) {
  101 + vm.filterProposals();
  102 + }
114 }); 103 });
115 }; 104 };
116 105
@@ -128,6 +117,35 @@ @@ -128,6 +117,35 @@
128 vm.filterProposals(pageIndex); 117 vm.filterProposals(pageIndex);
129 }; 118 };
130 119
  120 + PropostasPageController.prototype.filter = function() {
  121 + var vm = this;
  122 +
  123 + if (vm.loadingThemes || vm.loadingProposals) {
  124 + vm.$log.info('No proposals or themes loaded yet. Abort.');
  125 + return;
  126 + }
  127 +
  128 + if (vm._filtredByThemeSlug) {
  129 + var slug = vm._filtredByThemeSlug;
  130 +
  131 + vm.DialogaService.getThemeBySlug(slug, function(theme) {
  132 + vm.selectedTheme = theme;
  133 + }, function(error) {
  134 + vm.$log.error('Error when try to "getThemeBySlug"', error);
  135 + });
  136 + }
  137 +
  138 + if (vm._filtredByQuery) {
  139 + vm.query = vm._filtredByQuery;
  140 + }
  141 +
  142 + if (vm._filtredByThemeSlug || vm._filtredByQuery) {
  143 + vm.filterProposals();
  144 + vm.loadingFilter = false;
  145 + }
  146 +
  147 + };
  148 +
131 PropostasPageController.prototype.filterProposals = function(_page, _per_page) { 149 PropostasPageController.prototype.filterProposals = function(_page, _per_page) {
132 var vm = this; 150 var vm = this;
133 151
@@ -165,8 +183,6 @@ @@ -165,8 +183,6 @@
165 PropostasPageController.prototype.submitSearch = function() { 183 PropostasPageController.prototype.submitSearch = function() {
166 var vm = this; 184 var vm = this;
167 185
168 - vm.loadingFilter = true;  
169 -  
170 // scroll to result grid 186 // scroll to result grid
171 var $searchResult = angular.element('#search-result'); 187 var $searchResult = angular.element('#search-result');
172 if ($searchResult && $searchResult.length > 0) { 188 if ($searchResult && $searchResult.length > 0) {
src/app/pages/propostas/propostas.html
1 <div class="container"> 1 <div class="container">
2 - <div class="row">  
3 - <div class="col-sm-12">  
4 - <div ncy-breadcrumb></div> 2 + <div class="row">
  3 + <div class="col-sm-12">
  4 + <div ncy-breadcrumb></div>
  5 + </div>
5 </div> 6 </div>
6 - </div>  
7 </div> 7 </div>
8 -  
9 <div class="page--propostas" role="main"> 8 <div class="page--propostas" role="main">
10 - <section class="section-info" ng-if="pagePropostas.loading || pagePropostas.error">  
11 - <div class="container">  
12 - <div class="row">  
13 - <div class="col-md-12">  
14 - <div ng-if="pagePropostas.loading && !pagePropostas.error">  
15 - <div class="alert alert-info" role="alert">Carregando conteúdo...</div>  
16 - </div>  
17 -  
18 - <div ng-if="pagePropostas.error">  
19 - <div class="alert alert-danger" role="alert">  
20 - Erro ao carregar o conteúdo principal. 9 + <section class="section-info" ng-if="pagePropostas.loading || pagePropostas.error">
  10 + <div class="container">
  11 + <div class="row">
  12 + <div class="col-md-12">
  13 + <div ng-if="pagePropostas.loading && !pagePropostas.error">
  14 + <div class="alert alert-info" role="alert">Carregando conteúdo...</div>
  15 + </div>
  16 + <div ng-if="pagePropostas.error">
  17 + <div class="alert alert-danger" role="alert">
  18 + Erro ao carregar o conteúdo principal.
  19 + </div>
  20 + </div>
  21 + </div>
21 </div> 22 </div>
22 - </div>  
23 </div> 23 </div>
24 - </div>  
25 - </div>  
26 - </section>  
27 -  
28 - <section class="section--header" ng-if="pagePropostas.filtredProposals || pagePropostas.themes">  
29 - <div class="container">  
30 - <div class="row">  
31 - <div class="col-sm-12">  
32 - <h1>Propostas</h1>  
33 - </div>  
34 - </div>  
35 - </div>  
36 - </section>  
37 -  
38 - <section class="section--articles section-gray section-space-up" ng-if="pagePropostas.filtredProposals || pagePropostas.themes">  
39 - <div class="container">  
40 - <div id="lista-de-propostas" class="row">  
41 - <div class="col-sm-4 col-md-3">  
42 - <div class="row visible-xs">  
43 - <div class="col-xs-12">  
44 - <div class="input-group input-group-lg input-group-search">  
45 - <label for="articleQueryFilter" class="control-label sr-only">Buscar propostas:</label>  
46 - <input id="articleQueryFilter" type="search" class="form-control input-search" ng-model="pagePropostas.query" placeholder="Buscar propostas" aria-label="Buscar propostas">  
47 - <span class="input-group-btn">  
48 - <button type="button" class="btn btn-default" ng-click="pagePropostas.submitSearch()">  
49 - <span class="icon-circle icon-small color-theme-common-bg">  
50 - <span class="glyphicon glyphicon-search"></span>  
51 - </span>  
52 - <span class="sr-only">Buscar</span>  
53 - </button>  
54 - </span>  
55 - </div>  
56 - <br/>  
57 - </div>  
58 - </div>  
59 - <div ng-if="pagePropostas.themes">  
60 - <category-list categories="pagePropostas.themes" selected-category="pagePropostas.selectedTheme"></category-list>  
61 - </div>  
62 - <div ng-if="!pagePropostas.themes && pagePropostas.loadingThemes">  
63 - <div class="alert alert-info" role="alert">  
64 - Carregando temas. 24 + </section>
  25 + <section class="section--header" ng-if="pagePropostas.filtredProposals || pagePropostas.themes">
  26 + <div class="container">
  27 + <div class="row">
  28 + <div class="col-sm-12">
  29 + <h1>Propostas</h1>
  30 + </div>
65 </div> 31 </div>
66 - </div>  
67 - <div ng-if="!pagePropostas.themes && pagePropostas.themesError">  
68 - <div class="alert alert-danger" role="alert">  
69 - Não foi possível carregar a lista de temas neste momento.  
70 - </div>  
71 - </div>  
72 </div> 32 </div>
73 - <div class="col-sm-8 col-md-9">  
74 - <div class="row hidden-xs" ng-if="pagePropostas.filtredProposals">  
75 - <div class="col-xs-12">  
76 - <div class="input-group input-group-lg input-group-search">  
77 - <label for="articleQueryFilter" class="control-label sr-only">Buscar propostas:</label>  
78 - <input id="articleQueryFilter" type="search" class="form-control input-search" ng-model="pagePropostas.query" placeholder="Buscar propostas" aria-label="Buscar propostas">  
79 - <span class="input-group-btn">  
80 - <button type="button" class="btn btn-default" ng-click="pagePropostas.submitSearch()">  
81 - <span class="icon-circle icon-small color-theme-common-bg">  
82 - <span class="glyphicon glyphicon-search"></span>  
83 - </span>  
84 - <span class="sr-only">Buscar</span>  
85 - </button>  
86 - </span>  
87 - </div>  
88 - </div>  
89 - </div>  
90 -  
91 - <div id="search-result" class="row" ng-if="pagePropostas.filtredProposals">  
92 - <div class="col-sm-12">  
93 - <header class="header">  
94 - <h2>Total de Propostas: "<b>{{pagePropostas.total_proposals}} propostas</b>"</h2>  
95 - </header>  
96 - </div>  
97 - </div>  
98 -  
99 - <div class="row">  
100 - <div class="col-sm-12" ng-if="!pagePropostas.loadingProposals && pagePropostas.filtredProposals && pagePropostas.total_proposals">  
101 - <proposal-grid proposals="pagePropostas.filtredProposals"></proposal-grid>  
102 - <app-paginator  
103 - page="pagePropostas.page"  
104 - per-page="pagePropostas.per_page"  
105 - total="pagePropostas.total_proposals"  
106 - change-page="pagePropostas.changePage(pageIndex)"  
107 - ></app-paginator> 33 + </section>
  34 + <section class="section--articles section-gray section-space-up" ng-if="pagePropostas.filtredProposals || pagePropostas.themes">
  35 + <div class="container">
  36 + <div id="lista-de-propostas" class="row">
  37 + <div class="col-sm-4 col-md-3">
  38 + <div class="row visible-xs">
  39 + <div class="col-xs-12">
  40 + <div class="input-group input-group-lg input-group-search">
  41 + <label for="articleQueryFilter" class="control-label sr-only">Buscar propostas:</label>
  42 + <input id="articleQueryFilter" type="search" class="form-control input-search" ng-model="pagePropostas.query" placeholder="Buscar propostas" aria-label="Buscar propostas">
  43 + <div class="input-group-btn">
  44 + <button type="button" class="btn btn-default" ng-click="pagePropostas.submitSearch()">
  45 + <div class="icon-circle icon-small color-theme-common-bg">
  46 + <span class="glyphicon glyphicon-search"></span>
  47 + </div>
  48 + <span class="sr-only">Buscar</span>
  49 + </button>
  50 + </div>
  51 + </div>
  52 + <br/>
  53 + </div>
  54 + </div>
  55 + <div ng-if="pagePropostas.themes">
  56 + <category-list categories="pagePropostas.themes" selected-category="pagePropostas.selectedTheme"></category-list>
  57 + </div>
  58 + <div ng-if="!pagePropostas.themes && pagePropostas.loadingThemes">
  59 + <div class="alert alert-info" role="alert">
  60 + Carregando temas.
  61 + </div>
  62 + </div>
  63 + <div ng-if="!pagePropostas.themes && pagePropostas.themesError">
  64 + <div class="alert alert-danger" role="alert">
  65 + Não foi possível carregar a lista de temas neste momento.
  66 + </div>
  67 + </div>
  68 + </div>
  69 + <div class="col-sm-8 col-md-9">
  70 + <div class="row hidden-xs" ng-if="pagePropostas.filtredProposals">
  71 + <div class="col-xs-12">
  72 + <div class="input-group input-group-lg input-group-search">
  73 + <label for="articleQueryFilter" class="control-label sr-only">Buscar propostas:</label>
  74 + <input id="articleQueryFilter" type="search" class="form-control input-search" ng-model="pagePropostas.query" placeholder="Buscar propostas" aria-label="Buscar propostas">
  75 + <div class="input-group-btn">
  76 + <button type="button" class="btn btn-default" ng-click="pagePropostas.submitSearch()">
  77 + <div class="icon-circle icon-small color-theme-common-bg">
  78 + <span class="glyphicon glyphicon-search"></span>
  79 + </div>
  80 + <span class="sr-only">Buscar</span>
  81 + </button>
  82 + </div>
  83 + </div>
  84 + </div>
  85 + </div>
  86 + <div id="search-result" class="row" ng-if="pagePropostas.filtredProposals">
  87 + <div class="col-sm-12">
  88 + <header class="header">
  89 + <h2>Total de Propostas: "<b>{{pagePropostas.total_proposals}} propostas</b>"</h2>
  90 + </header>
  91 + </div>
  92 + </div>
  93 + <div class="row">
  94 + <div class="col-sm-12" ng-if="!pagePropostas.loadingProposals && pagePropostas.filtredProposals && pagePropostas.total_proposals">
  95 + <proposal-grid proposals="pagePropostas.filtredProposals"></proposal-grid>
  96 + <app-paginator page="pagePropostas.page" per-page="pagePropostas.per_page" total="pagePropostas.total_proposals" change-page="pagePropostas.changePage(pageIndex)"></app-paginator>
  97 + </div>
  98 + <div ng-if="pagePropostas.loadingProposals">
  99 + <div class="alert alert-info" role="alert">
  100 + Carregando propostas.
  101 + </div>
  102 + </div>
  103 + <div ng-if="!pagePropostas.loadingProposals && pagePropostas.proposalsError">
  104 + <div class="alert alert-danger" role="alert">
  105 + Não foi possível carregar a lista de propostas neste momento.
  106 + </div>
  107 + </div>
  108 + </div>
  109 + </div>
108 </div> 110 </div>
109 - <div ng-if="pagePropostas.loadingProposals">  
110 - <div class="alert alert-info" role="alert">  
111 - Carregando propostas.  
112 - </div>  
113 - </div>  
114 - <div ng-if="!pagePropostas.loadingProposals && pagePropostas.proposalsError">  
115 - <div class="alert alert-danger" role="alert">  
116 - Não foi possível carregar a lista de propostas neste momento.  
117 - </div>  
118 - </div>  
119 - </div>  
120 </div> 111 </div>
121 - </div>  
122 - </div>  
123 - </section> 112 + </section>
124 </div> 113 </div>
src/app/pages/respostas/respostas.controller.js 0 → 100644
@@ -0,0 +1,196 @@ @@ -0,0 +1,196 @@
  1 +(function() {
  2 + 'use strict';
  3 +
  4 + angular
  5 + .module('dialoga')
  6 + .controller('RespostasPageController', RespostasPageController);
  7 +
  8 + /** @ngInject */
  9 + function RespostasPageController(DialogaService, $scope, $rootScope, $location, $filter, $log) {
  10 + var vm = this;
  11 +
  12 + vm.DialogaService = DialogaService;
  13 + vm.$scope = $scope;
  14 + vm.$rootScope = $rootScope;
  15 + vm.$location = $location;
  16 + vm.$filter = $filter;
  17 + vm.$log = $log;
  18 +
  19 + vm.init();
  20 + vm.loadData();
  21 + vm.attachListeners();
  22 + vm.$rootScope.focusMainContent();
  23 +
  24 + $log.debug('RespostasPageController');
  25 + }
  26 +
  27 + RespostasPageController.prototype.init = function() {
  28 + var vm = this;
  29 +
  30 + vm.page = 1;
  31 + vm.per_page = 10;
  32 + vm.themes = null;
  33 + vm.selectedTheme = null;
  34 + vm.filtredProposals = null;
  35 + vm.query = null;
  36 + vm.search = vm.$location.search();
  37 +
  38 + if (vm.search.tema) {
  39 + vm._filtredByThemeSlug = vm.search.tema;
  40 + }
  41 +
  42 + if (vm.search.filtro) {
  43 + vm._filtredByQuery = vm.search.filtro;
  44 + }
  45 +
  46 + if (vm.search.tema || vm.search.filtro) {
  47 + vm.loadingFilter = true;
  48 + }
  49 +
  50 + vm.loading = null;
  51 + vm.error = null;
  52 + };
  53 +
  54 + RespostasPageController.prototype.loadData = function() {
  55 + var vm = this;
  56 +
  57 + vm.loading = true;
  58 +
  59 + // Behaviour:
  60 + // 1. Load themes
  61 + // 1. Load Proposals per_page
  62 + // END.
  63 +
  64 + // 1. Load themes
  65 + vm.loadingThemes = true;
  66 + vm.DialogaService.getThemes(function(themes) {
  67 + vm.themes = themes;
  68 + vm.loadingThemes = false;
  69 + vm.loading = false;
  70 +
  71 + vm.filter();
  72 + }, function(error) {
  73 + vm.error = error;
  74 + vm.$log.error(error);
  75 + vm.loadingThemes = false;
  76 + vm.loading = false;
  77 + });
  78 + };
  79 +
  80 + RespostasPageController.prototype.attachListeners = function() {
  81 + var vm = this;
  82 +
  83 + vm.$scope.$on('change-selectedCategory', function(event, selectedCategory) {
  84 + vm.selectedTheme = selectedCategory;
  85 + });
  86 +
  87 + vm.$scope.$watch('pageRespostas.selectedTheme', function(newValue/*, oldValue*/) {
  88 + vm.search.tema = newValue ? newValue.slug : null;
  89 + vm.$location.search('tema', vm.search.tema);
  90 +
  91 + if (!vm.loadingFilter) {
  92 + vm.filterProposals();
  93 + }
  94 + });
  95 +
  96 + vm.$scope.$watch('pageRespostas.query', function(newValue/*, oldValue*/) {
  97 + vm.search.filtro = newValue ? newValue : null;
  98 + vm.$location.search('filtro', vm.search.filtro);
  99 +
  100 + if (!vm.loadingFilter) {
  101 + vm.filterProposals();
  102 + }
  103 + });
  104 + };
  105 +
  106 + RespostasPageController.prototype.resetFilterValues = function() {
  107 + var vm = this;
  108 +
  109 + vm.query = null;
  110 + vm.selectedTheme = null;
  111 + };
  112 +
  113 + RespostasPageController.prototype.changePage = function(pageIndex) {
  114 + var vm = this;
  115 +
  116 + vm.page = pageIndex;
  117 + vm.filterProposals(pageIndex);
  118 + };
  119 +
  120 + RespostasPageController.prototype.filter = function() {
  121 + var vm = this;
  122 +
  123 + if (vm.loadingThemes || vm.loadingProposals) {
  124 + vm.$log.info('No proposals or themes loaded yet. Abort.');
  125 + return;
  126 + }
  127 +
  128 + if (vm._filtredByThemeSlug) {
  129 + var slug = vm._filtredByThemeSlug;
  130 +
  131 + vm.DialogaService.getThemeBySlug(slug, function(theme) {
  132 + vm.selectedTheme = theme;
  133 + }, function(error) {
  134 + vm.$log.error('Error when try to "getThemeBySlug"', error);
  135 + });
  136 + }
  137 +
  138 + if (vm._filtredByQuery) {
  139 + vm.query = vm._filtredByQuery;
  140 + }
  141 +
  142 + if (vm._filtredByThemeSlug || vm._filtredByQuery) {
  143 + vm.filterProposals();
  144 + vm.loadingFilter = false;
  145 + }
  146 + };
  147 +
  148 + RespostasPageController.prototype.filterProposals = function(_page, _per_page) {
  149 + var vm = this;
  150 +
  151 + if (vm.loadingProposals){
  152 + vm.$log.debug('Content is not loaded yet.');
  153 + return;
  154 + }
  155 +
  156 + var page = _page || vm.page;
  157 + var per_page = _per_page || vm.per_page;
  158 + var query = vm.query;
  159 + var params = {
  160 + page: page,
  161 + per_page: per_page,
  162 + archived: true
  163 + };
  164 +
  165 + if (vm.selectedTheme) {
  166 + params.category_ids = vm.selectedTheme.id;
  167 + }
  168 +
  169 + if (query) {params.query = query; }
  170 +
  171 + vm.loadingProposals = true;
  172 + vm.DialogaService.searchProposals(params, function(data){
  173 + vm.total_proposals = parseInt(data._obj.headers('total'));
  174 + vm.filtredProposals = data.articles;
  175 + vm.loadingProposals = false;
  176 + }, function (error) {
  177 + vm.error = error;
  178 + vm.$log.error(error);
  179 + vm.loadingProposals = false;
  180 + });
  181 + };
  182 +
  183 + RespostasPageController.prototype.submitSearch = function() {
  184 + var vm = this;
  185 +
  186 + // scroll to result grid
  187 + var $searchResult = angular.element('#search-result');
  188 + if ($searchResult && $searchResult.length > 0) {
  189 + angular.element('html,body').animate({scrollTop: $searchResult.offset().top}, 'fast');
  190 + vm.filterProposals();
  191 + }else {
  192 + vm.$log.warn('#search-result element not found.');
  193 + }
  194 + };
  195 +
  196 +})();
src/app/pages/respostas/respostas.html 0 → 100644
@@ -0,0 +1,117 @@ @@ -0,0 +1,117 @@
  1 +<div class="container">
  2 + <div class="row">
  3 + <div class="col-sm-12">
  4 + <div ncy-breadcrumb></div>
  5 + </div>
  6 + </div>
  7 +</div>
  8 +<div class="page--respostas" role="main">
  9 + <section class="section-info" ng-if="pageRespostas.loading || pageRespostas.error">
  10 + <div class="container">
  11 + <div class="row">
  12 + <div class="col-md-12">
  13 + <div ng-if="pageRespostas.loading && !pageRespostas.error">
  14 + <div class="alert alert-info" role="alert">Carregando conteúdo...</div>
  15 + </div>
  16 + <div ng-if="pageRespostas.error">
  17 + <div class="alert alert-danger" role="alert">
  18 + Erro ao carregar o conteúdo principal.
  19 + </div>
  20 + </div>
  21 + </div>
  22 + </div>
  23 + </div>
  24 + </section>
  25 + <section class="section--header" ng-if="pageRespostas.filtredProposals || pageRespostas.themes">
  26 + <div class="container">
  27 + <div class="row">
  28 + <div class="col-sm-12">
  29 + <h1>O Dialoga Brasil já respondeu às solicitações!</h1>
  30 + <p>
  31 + Vários programas contaram com centenas de participações e elas foram respondidas pelo governo.
  32 + Confira abaixo as propostas que foram respondidas:
  33 + </p>
  34 + </div>
  35 + </div>
  36 + </div>
  37 + </section>
  38 + <section class="section--articles section-gray section-space-up" ng-if="pageRespostas.filtredProposals || pageRespostas.themes">
  39 + <div class="container">
  40 + <div id="lista-de-respostas" class="row">
  41 + <div class="col-sm-4 col-md-3">
  42 + <div class="row visible-xs">
  43 + <div class="col-xs-12">
  44 + <div class="input-group input-group-lg input-group-search">
  45 + <label for="articleQueryFilter" class="control-label sr-only">Buscar respostas:</label>
  46 + <input id="articleQueryFilter" type="search" class="form-control input-search" ng-model="pageRespostas.query" placeholder="Buscar respostas" aria-label="Buscar respostas">
  47 + <div class="input-group-btn">
  48 + <button type="button" class="btn btn-default" ng-click="pageRespostas.submitSearch()">
  49 + <div class="icon-circle icon-small color-theme-common-bg">
  50 + <span class="glyphicon glyphicon-search"></span>
  51 + </div>
  52 + <span class="sr-only">Buscar</span>
  53 + </button>
  54 + </div>
  55 + </div>
  56 + <br/>
  57 + </div>
  58 + </div>
  59 + <div ng-if="pageRespostas.themes">
  60 + <category-list categories="pageRespostas.themes" selected-category="pageRespostas.selectedTheme"></category-list>
  61 + </div>
  62 + <div ng-if="!pageRespostas.themes && pageRespostas.loadingThemes">
  63 + <div class="alert alert-info" role="alert">
  64 + Carregando temas.
  65 + </div>
  66 + </div>
  67 + <div ng-if="!pageRespostas.themes && pageRespostas.themesError">
  68 + <div class="alert alert-danger" role="alert">
  69 + Não foi possível carregar a lista de temas neste momento.
  70 + </div>
  71 + </div>
  72 + </div>
  73 + <div class="col-sm-8 col-md-9">
  74 + <div class="row hidden-xs" ng-if="pageRespostas.filtredProposals">
  75 + <div class="col-xs-12">
  76 + <div class="input-group input-group-lg input-group-search">
  77 + <label for="articleQueryFilter" class="control-label sr-only">Buscar propostas:</label>
  78 + <input id="articleQueryFilter" type="search" class="form-control input-search" ng-model="pageRespostas.query" placeholder="Buscar respostas" aria-label="Buscar respostas">
  79 + <div class="input-group-btn">
  80 + <button type="button" class="btn btn-default" ng-click="pageRespostas.submitSearch()">
  81 + <div class="icon-circle icon-small color-theme-common-bg">
  82 + <span class="glyphicon glyphicon-search"></span>
  83 + </div>
  84 + <span class="sr-only">Buscar</span>
  85 + </button>
  86 + </div>
  87 + </div>
  88 + </div>
  89 + </div>
  90 + <div id="search-result" class="row" ng-if="pageRespostas.filtredProposals">
  91 + <div class="col-sm-12">
  92 + <header class="header">
  93 + <h2>Total de Propostas: "<b>{{pageRespostas.total_proposals}} propostas</b>"</h2>
  94 + </header>
  95 + </div>
  96 + </div>
  97 + <div class="row">
  98 + <div class="col-sm-12" ng-if="!pageRespostas.loadingProposals && pageRespostas.filtredProposals && pageRespostas.total_proposals">
  99 + <proposal-grid proposals="pageRespostas.filtredProposals"></proposal-grid>
  100 + <app-paginator page="pageRespostas.page" per-page="pageRespostas.per_page" total="pageRespostas.total_proposals" change-page="pageRespostas.changePage(pageIndex)"></app-paginator>
  101 + </div>
  102 + <div ng-if="pageRespostas.loadingProposals">
  103 + <div class="alert alert-info" role="alert">
  104 + Carregando propostas.
  105 + </div>
  106 + </div>
  107 + <div ng-if="!pageRespostas.loadingProposals && pageRespostas.proposalsError">
  108 + <div class="alert alert-danger" role="alert">
  109 + Não foi possível carregar a lista de propostas neste momento.
  110 + </div>
  111 + </div>
  112 + </div>
  113 + </div>
  114 + </div>
  115 + </div>
  116 + </section>
  117 +</div>
src/app/pages/respostas/respostas.scss 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +.page--respostas {
  2 + .proposal-box--middle {
  3 + background-color: #fff;
  4 + }
  5 +
  6 + .topics-select--wrapper {
  7 + margin: 20px 0;
  8 + }
  9 +}