Commit c07e7c4b42e81fc477a1ce00108867a1dd7e3723

Authored by Leonardo Merlin
1 parent e80daf58

Sync

Showing 27 changed files with 893 additions and 864 deletions   Show diff stats
src/app/components/article-grid/article-grid.directive.js
... ... @@ -26,7 +26,6 @@
26 26  
27 27 // initialization
28 28 vm.init();
29   - vm.loadData();
30 29 vm.attachListeners();
31 30 }
32 31  
... ... @@ -35,10 +34,6 @@
35 34 // vm.programs = null; // scope var
36 35 };
37 36  
38   - ArticleGridController.prototype.loadData = function() {
39   - // var vm = this;
40   - };
41   -
42 37 ArticleGridController.prototype.attachListeners = function() {
43 38 // var vm = this;
44 39 };
... ...
src/app/components/article-service/article.service.js
... ... @@ -17,6 +17,8 @@
17 17 getCategoryBySlug: getCategoryBySlug,
18 18 getTopics: getTopics,
19 19 getTopicById: getTopicById,
  20 + getProposals: getProposals,
  21 + getProposalsByTopicId: getProposalsByTopicId,
20 22 searchTopics: searchTopics,
21 23 searchProposals: searchProposals
22 24 };
... ... @@ -90,6 +92,40 @@
90 92 });
91 93 }
92 94  
  95 + function getProposals (params, cbSuccess, cbError) {
  96 + // Ex.: /api/v1/articles/103358?fields=
  97 +
  98 + var url = service.apiArticles + API.articleId.home;
  99 +
  100 + var paramsExtended = angular.extend({
  101 + 'fields[]': ['id', 'title', 'slug', 'abstract', 'categories', 'setting', 'children_count', 'hits'],
  102 + 'content_type':'ProposalsDiscussionPlugin::Proposals'
  103 + }, params);
  104 +
  105 + UtilService.get(url, {params: paramsExtended}).then(function(data){
  106 + cbSuccess(data);
  107 + }).catch(function(error){
  108 + cbError(error);
  109 + });
  110 + }
  111 +
  112 + function getProposalsByTopicId (topicId, params, cbSuccess, cbError) {
  113 + var url = service.apiArticles + topicId;
  114 +
  115 + var paramsExtended = angular.extend({
  116 + 'fields[]': ['id', 'title', 'abstract', 'children', 'children_count'],
  117 + 'content_type':'ProposalsDiscussionPlugin::Proposals'
  118 + }, params);
  119 +
  120 + UtilService.get(url, {params: paramsExtended}).then(function(data){
  121 + cbSuccess(data);
  122 + }).catch(function(error){
  123 + cbError(error);
  124 + });
  125 + }
  126 +
  127 + function getRandomProposal (cbSuccess, cbError) {}
  128 +
93 129 function searchTopics (params, cbSuccess, cbError) {
94 130 // Ex.: /api/v1/search/article?type=ProposalsDiscussionPlugin::Topic&query=cisternas
95 131 var url = '/api/v1/search/article';
... ...
src/app/components/dialoga-service/dialoga.service.js
... ... @@ -17,6 +17,7 @@
17 17 getProgramBySlug: getProgramBySlug,
18 18 getProgramsRandom: getProgramsRandom,
19 19 getProposals: getProposals,
  20 + getProposalsByTopicId: getProposalsByTopicId,
20 21 getEvents: getEvents,
21 22 getQuestions: getQuestions,
22 23 searchProgram: searchProgram,
... ... @@ -131,18 +132,20 @@
131 132 // }
132 133 }
133 134  
134   - function getProposals (cbSuccess, cbError) {
135   - if( !!CACHE.proposals ){
  135 + function getProposals (param, cbSuccess, cbError) {
  136 + ArticleService.getProposals(param, function (data){
  137 + CACHE.proposals = data;
  138 +
136 139 cbSuccess(CACHE.proposals);
137   - }else{
138   - // load main content
139   - getHome(function(){
140   - if(!CACHE.hasOwnProperty('proposals')){
141   - throw { name: 'NotFound', message: '"proposals" is not defined. "article.categories" was loaded?'};
142   - }
143   - cbSuccess(CACHE.proposals);
144   - },cbError);
145   - }
  140 + }, cbError);
  141 + }
  142 +
  143 + function getProposalsByTopicId (topicId, params, cbSuccess, cbError) {
  144 + ArticleService.getProposalsByTopicId(topicId, params, function (data){
  145 + CACHE.proposals = data;
  146 +
  147 + cbSuccess(CACHE.proposals);
  148 + }, cbError);
146 149 }
147 150  
148 151 function getEvents (cbSuccess, cbError) {
... ...
src/app/components/programas/programas.directive.js
... ... @@ -1,316 +0,0 @@
1   -(function() {
2   - 'use strict';
3   -
4   - angular
5   - .module('dialoga')
6   - .directive('programaList', programaList);
7   -
8   - /** @ngInject */
9   - function programaList() {
10   -
11   - /** @ngInject */
12   - function ProgramaListController($scope, $element, $location, $filter, $log) {
13   - $log.debug('ProgramaListController');
14   -
15   - // alias
16   - var vm = this;
17   -
18   - // dependencies
19   - vm.$scope = $scope;
20   - vm.$element = $element;
21   - vm.$location = $location;
22   - vm.$filter = $filter;
23   - vm.$log = $log;
24   - vm.defaultLimit = 6;
25   -
26   - // initialization
27   - vm.init();
28   - }
29   -
30   - ProgramaListController.prototype.init = function() {
31   - var vm = this;
32   -
33   - if (!vm.article) {
34   - vm.$log.warn('no article to display. Tip: use a ng-if before use this directive');
35   - return;
36   - }
37   -
38   - vm.categories = vm.article.categories;
39   - vm.programs = vm.article.children;
40   - vm.orderCriteries = [
41   - { label: 'Título', name: 'titulo' },
42   - { label: 'Tema', name: 'tema' },
43   - { label: 'Aleatório', name: 'aleatorio' }
44   - ];
45   -
46   - vm.filtredProgramList = vm.getFiltredPrograms();
47   - vm.search = vm.$location.search();
48   -
49   - // Add initial values for the filter
50   - vm.query = (vm.search && vm.search.filtro) ? vm.search.filtro : null;
51   - vm.limitTo = (vm.search && vm.search.limite) ? parseInt(vm.search.limite, 10) : vm.defaultLimit;
52   - vm.categoryFilter = (vm.search && vm.search.tema) ? vm.getCategoryBySlug(vm.search.tema) : null;
53   - vm.orderCriteria = (vm.search && vm.search.ordem) ? { name: vm.search.ordem } : null;
54   - vm.reverse = (vm.search && vm.search.reverso) ? true : false;
55   -
56   - if (!angular.equals({}, vm.search)) {
57   - var $el = vm.$element;
58   - angular.element('body').animate({scrollTop: $el.offset().top}, 'slow');
59   - }
60   -
61   - // update window location params
62   - vm.$scope.$watch('vm.query', function(newValue/*, oldValue*/) {
63   - vm.search.filtro = newValue ? newValue : null;
64   - vm.$location.search('filtro', vm.search.filtro);
65   - if(vm.search.filtro){
66   - vm.limitTo = vm.programs.length;
67   - }else{
68   - vm.limitTo = vm.defaultLimit;
69   - }
70   - vm.filtredProgramList = vm.getFiltredPrograms();
71   - });
72   -
73   - vm.$scope.$watch('vm.limitTo', function(newValue/*, oldValue*/) {
74   - vm.search.limite = (newValue && newValue !== vm.defaultLimit) ? newValue : null;
75   - vm.$location.search('limite', vm.search.limite);
76   - vm.filtredProgramList = vm.getFiltredPrograms();
77   - });
78   -
79   - vm.$scope.$watch('vm.categoryFilter', function(newValue/*, oldValue*/) {
80   - vm.search.tema = newValue ? newValue.slug : null;
81   - vm.$location.search('tema', vm.search.tema);
82   - if(vm.search.tema){
83   - vm.limitTo = vm.programs.length;
84   - }
85   - vm.filtredProgramList = vm.getFiltredPrograms();
86   - });
87   -
88   - vm.$scope.$watch('vm.orderCriteria', function(newValue/*, oldValue*/) {
89   - vm.search.ordem = (newValue && newValue.name) ? newValue.name : null;
90   - vm.$location.search('ordem', vm.search.ordem);
91   - vm.filtredProgramList = vm.getFiltredPrograms();
92   - });
93   -
94   - vm.$scope.$watch('vm.reverse', function(newValue/*, oldValue*/) {
95   - vm.search.reverso = newValue ? newValue : null;
96   - vm.$location.search('reverso', vm.search.reverso);
97   - vm.filtredProgramList = vm.getFiltredPrograms();
98   - });
99   -
100   - };
101   -
102   - ProgramaListController.prototype.resetFilterValues = function() {
103   - var vm = this;
104   -
105   - vm.query = null;
106   - vm.limitTo = vm.defaultLimit;
107   - vm.categoryFilter = null;
108   - vm.orderCriteria = null;
109   - };
110   -
111   - ProgramaListController.prototype.getIconClasses = function(category) {
112   - var vm = this;
113   -
114   - vm.$log.debug('[TODO] getIconClasses of category:', category);
115   - return 'glyphicon glyphicon-exclamation-sign';
116   - };
117   -
118   - ProgramaListController.prototype.getCategoryBySlug = function(categorySlug) {
119   - var vm = this;
120   - var result = null;
121   -
122   - angular.forEach(vm.categories, function(value/*, key*/) {
123   - if (value.slug === categorySlug) {
124   - result = value;
125   - }
126   - });
127   -
128   - return result;
129   - };
130   -
131   - ProgramaListController.prototype.filterByCategory = function(category, $event) {
132   - var vm = this;
133   -
134   - $event.stopPropagation();
135   -
136   - if (category !== vm.categoryFilter) {
137   -
138   - // selected new filter
139   - vm.categoryFilter = category;
140   - } else {
141   - vm.categoryFilter = null;
142   - }
143   - };
144   -
145   - ProgramaListController.prototype.showAll = function($event) {
146   - var vm = this;
147   -
148   - $event.stopPropagation();
149   -
150   - vm.resetFilterValues();
151   - vm.limitTo = vm.programs.length;
152   - };
153   -
154   - ProgramaListController.prototype.getFiltredPrograms = function() {
155   - var vm = this;
156   -
157   - var input = vm.programs;
158   - var output = input;
159   - var query = vm.query;
160   - var categoryFilter = vm.categoryFilter;
161   - var orderCriteria = vm.orderCriteria ? vm.orderCriteria : { name : 'aleatorio'};
162   - var filter = vm.$filter('filter');
163   - var orderBy = vm.$filter('orderBy');
164   - var limitTo = vm.$filter('limitTo');
165   - var limit = vm.limitTo ? vm.limitTo : 4;
166   -
167   - if (categoryFilter) {
168   - output = _filterByCategory(output, categoryFilter);
169   - }
170   -
171   - if (query) {
172   - output = filter(output, query, false);
173   - }
174   -
175   - switch (orderCriteria.name) {
176   - case 'titulo':
177   - output = orderBy(output, 'title', vm.reverse);
178   - break;
179   - case 'tema':
180   - output = orderBy(output, 'categories[0].name', vm.reverse);
181   - break;
182   - case 'more_participants':
183   - vm.$log.info('Criteria not handled yet: ', orderCriteria);
184   - break;
185   - case 'aleatorio':
186   - // shuffling
187   - // if (!vm._isShuffled){
188   - output = vm.filterShuffle(output);
189   - // vm._isShuffled = true;
190   - // }
191   -
192   - if (vm.reverse) {
193   - output = output.slice().reverse();
194   - }
195   -
196   - break;
197   - default:
198   - vm.$log.warn('Criteria not matched: ', orderCriteria);
199   - break;
200   - }
201   -
202   - output = limitTo(output, limit);
203   -
204   - return output;
205   - };
206   -
207   - ProgramaListController.prototype.filterShuffle = function(input) {
208   - var result = [];
209   - var resultByCategory = {};
210   -
211   - // divide by categories
212   - for (var i = 0; i < input.length; i++) {
213   - var program = input[i];
214   - var categorySlug = program.categories[0].slug;
215   -
216   - if (!resultByCategory[categorySlug]) {
217   - resultByCategory[categorySlug] = [];
218   - }
219   -
220   - resultByCategory[categorySlug].push(program);
221   - }
222   -
223   - // shuffle each array
224   - var prop = null;
225   - var categoryWithPrograms = null;
226   - for (prop in resultByCategory) {
227   - if (resultByCategory.hasOwnProperty(prop)) {
228   - categoryWithPrograms = resultByCategory[prop];
229   - resultByCategory[prop] = shuffle(categoryWithPrograms);
230   - }
231   - }
232   -
233   - // Concat all into result array
234   - // > while has program at Lists on resultByCategory
235   - var hasProgram = true;
236   - while (hasProgram) {
237   -
238   - var foundProgram = false;
239   - // each categoryList with array of program
240   - prop = null;
241   - categoryWithPrograms = null;
242   - for (prop in resultByCategory) {
243   -
244   - if (resultByCategory.hasOwnProperty(prop)) {
245   - categoryWithPrograms = resultByCategory[prop];
246   -
247   - if (categoryWithPrograms.length > 0) {
248   - var pivotProgram = categoryWithPrograms.pop();
249   - result.push(pivotProgram);
250   - foundProgram = true;
251   - }
252   - }
253   - }
254   -
255   - if (!foundProgram) {
256   - hasProgram = false;
257   - }
258   - }
259   -
260   - return result;
261   - };
262   -
263   - var directive = {
264   - restrict: 'E',
265   - templateUrl: 'app/components/programas/programas.html',
266   - scope: {
267   - article: '='
268   - },
269   - controller: ProgramaListController,
270   - controllerAs: 'vm',
271   - bindToController: true
272   - };
273   -
274   - return directive;
275   - }
276   -
277   - function _filterByCategory (input, category) {
278   - input = input || [];
279   -
280   - if (!category) {
281   - // no filter
282   - return input;
283   - }
284   -
285   - var out = [];
286   - for (var i = 0; i < input.length; i++) {
287   - var program = input[i];
288   - if (program.categories[0].slug === category.slug) {
289   - out.push(program);
290   - }
291   - }
292   -
293   - return out;
294   - }
295   -
296   - // -> Fisher–Yates shuffle algorithm
297   - function shuffle (array) {
298   - var currentIndex = array.length, temporaryValue, randomIndex ;
299   -
300   - // While there remain elements to shuffle...
301   - while (0 !== currentIndex) {
302   -
303   - // Pick a remaining element...
304   - randomIndex = Math.floor(Math.random() * currentIndex);
305   - currentIndex -= 1;
306   -
307   - // And swap it with the current element.
308   - temporaryValue = array[currentIndex];
309   - array[currentIndex] = array[randomIndex];
310   - array[randomIndex] = temporaryValue;
311   - }
312   -
313   - return array;
314   - }
315   -
316   -})();
src/app/components/programas/programas.directive.spec.js
... ... @@ -1,32 +0,0 @@
1   -(function() {
2   - 'use strict';
3   -
4   - describe('program directive', function() {
5   - var compile, scope, directiveElem;
6   -
7   - beforeEach(function(){
8   - module('dialoga');
9   -
10   - inject(function($compile, $rootScope){
11   - compile = $compile;
12   - scope = $rootScope.$new();
13   - // mock article
14   - scope.article = {};
15   - });
16   -
17   - directiveElem = getCompiledElement();
18   - });
19   -
20   - function getCompiledElement(){
21   - var element = compile(angular.element('<programa-list></programa-list>'))(scope);
22   - var compiledElement = compile(element)(scope);
23   - scope.$digest();
24   - return compiledElement;
25   - }
26   -
27   - it('ensure exist only one id "lista-de-programas"', function() {});
28   -
29   - it('should show default programs, one each category', function() {});
30   -
31   - });
32   -})();
src/app/components/proposal-carousel/proposal-carousel.directive.js 0 → 100644
... ... @@ -0,0 +1,71 @@
  1 +(function() {
  2 + 'use strict';
  3 +
  4 + angular
  5 + .module('dialoga')
  6 + .directive('proposalCarousel', proposalCarousel);
  7 +
  8 + /** @ngInject */
  9 + function proposalCarousel() {
  10 +
  11 + /** @ngInject */
  12 + function ProposalCarouselController($scope, $element, $timeout, $log) {
  13 + $log.debug('ProposalCarouselController');
  14 +
  15 + var vm = this;
  16 + vm.$scope = $scope;
  17 + vm.$element = $element;
  18 + vm.$timeout = $timeout;
  19 + vm.$log = $log;
  20 +
  21 + vm.init();
  22 + }
  23 +
  24 + ProposalCarouselController.prototype.init = function () {
  25 + // initial values
  26 + var vm = this;
  27 +
  28 + if(!vm.proposals){
  29 + throw { name: 'NotDefined', message: 'The attribute "proposals" is undefined.'};
  30 + }
  31 +
  32 + vm.activeIndex = 0;
  33 + vm.loading = false;
  34 + vm.proposalsLength = vm.proposals.length;
  35 + };
  36 +
  37 + ProposalCarouselController.prototype.swipeLeft = function () {
  38 + var vm = this;
  39 +
  40 + vm.activeIndex = (vm.activeIndex < vm.proposalsLength - 1) ? ++vm.activeIndex : 0;
  41 + };
  42 +
  43 + ProposalCarouselController.prototype.swipeRight = function () {
  44 + var vm = this;
  45 +
  46 + vm.activeIndex = (vm.activeIndex > 0) ? --vm.activeIndex : vm.proposalsLength - 1;
  47 + };
  48 +
  49 + ProposalCarouselController.prototype.showProposals = function () {
  50 + var vm = this;
  51 +
  52 + // notify parents - handled by parents
  53 + vm.$scope.$emit('proposal-carousel:toProposals');
  54 + };
  55 +
  56 + var directive = {
  57 + restrict: 'E',
  58 + templateUrl: 'app/components/proposal-carousel/proposal-carousel.html',
  59 + scope: {
  60 + proposals: '='
  61 + },
  62 + controller: ProposalCarouselController,
  63 + controllerAs: 'vm',
  64 + bindToController: true
  65 + };
  66 +
  67 +
  68 + return directive;
  69 + }
  70 +
  71 +})();
... ...
src/app/components/proposal-carousel/proposal-carousel.html 0 → 100644
... ... @@ -0,0 +1,33 @@
  1 +<div class="proposal-carousel">
  2 + <div ng-if="vm.proposals">
  3 + <div class="proposal-carousel-top color-theme-bg">
  4 + <div class="proposal-carousel-position" ng-repeat="proposal in vm.proposals">
  5 + <span ng-show="vm.activeIndex === $index">{{::($index+1)}}º</span>
  6 + <span ng-show="vm.activeIndex === $index">Lugar</span>
  7 + </div>
  8 + <div class="proposal-carousel-top-triggers" ng-if="vm.proposals">
  9 + <ul class="list-inline">
  10 + <li class="item-dot" ng-repeat="proposal in vm.proposals">
  11 + <button type="button" ng-class="{'active': vm.activeIndex === $index}" ng-click="vm.switchProposal($index)" title="Apersentar proposta na posição {{$index}}"></button>
  12 + </li>
  13 + </ul>
  14 + </div>
  15 + </div>
  16 + <div class="proposal-carousel-middle" ng-swipe-left="vm.swipeLeft()" ng-swipe-right="vm.swipeRight()">
  17 + <div ng-repeat="proposal in vm.proposals" class="animation-swipe">
  18 + <div class="content">
  19 + <div ng-show="vm.activeIndex === $index">{{::proposal.abstract}}</div>
  20 + </div>
  21 + <div class="proposal-carousel-middle-watermark" ng-show="vm.activeIndex === $index">
  22 + <span>{{::($index+1)}}º</span>
  23 + </div>
  24 + </div>
  25 + </div>
  26 + <div class="proposal-carousel-bottom color-theme-common-bg" ng-click="vm.showProposals()">
  27 + <div>Veja as propostas mais vortadas</div>
  28 + <div class="proposal-carousel-bottom-icon">
  29 + <span class="glyphicon glyphicon-chevron-down pull-right color-theme-common-fg"></span>
  30 + </div>
  31 + </div>
  32 + </div>
  33 +</div>
... ...
src/app/components/proposal-carousel/proposal-carousel.scss 0 → 100644
... ... @@ -0,0 +1,86 @@
  1 +.proposal-carousel {
  2 + background-color: #f1f1f1;
  3 + border-radius: 5px;
  4 + overflow: hidden;
  5 +
  6 + &-top {
  7 + position: relative;
  8 + color: #fff;
  9 + font-weight: bold;
  10 + font-size: 25px;
  11 + padding: 20px 15px;
  12 +
  13 + &-triggers {
  14 + position: absolute;
  15 + right: 15px;
  16 + top: 20px;
  17 +
  18 + button {
  19 + border: 1px solid #fff;
  20 + border-radius: 100%;
  21 + width: 15px;
  22 + height: 15px;
  23 + margin-right: 5px;
  24 + background-color: transparent;
  25 +
  26 + cursor: pointer;
  27 +
  28 + &.active {
  29 + background-color: #fff;
  30 + }
  31 + }
  32 + }
  33 + }
  34 +
  35 + &-middle {
  36 + position: relative;
  37 + padding: 25px 30px;
  38 + min-height: 200px;
  39 + // cursor: pointer;
  40 + -webkit-user-select: none;
  41 + -khtml-user-select: none;
  42 + -moz-user-select: none;
  43 + -ms-user-select: none;
  44 + user-select: none;
  45 +
  46 + .content {
  47 + position: relative;
  48 + z-index: 2;
  49 + }
  50 +
  51 + &-watermark {
  52 + position: absolute;
  53 + bottom: 1px;
  54 + left: -5px;
  55 + color: #ddd;
  56 + font-size: 150px;
  57 + font-weight: bold;
  58 + line-height: 116px;
  59 + z-index: 1;
  60 +
  61 + }
  62 + }
  63 +
  64 + &-bottom {
  65 + position: relative;
  66 + color: #fff;
  67 + padding: 10px;
  68 + font-weight: bold;
  69 + cursor: pointer;
  70 + z-index: 10;
  71 +
  72 + &-icon {
  73 + position: absolute;
  74 + top: 10px;
  75 + right: 15px;
  76 +
  77 + .glyphicon {
  78 + position: relative;
  79 + top: -2px;
  80 + background-color: #fff;
  81 + padding: 6px 5px 5px 6px;
  82 + border-radius: 100%;
  83 + }
  84 + }
  85 + }
  86 +}
... ...
src/app/components/proposal-list/proposal-list.directive.js 0 → 100644
... ... @@ -0,0 +1,81 @@
  1 +(function() {
  2 + 'use strict';
  3 +
  4 + angular
  5 + .module('dialoga')
  6 + .directive('proposalList', proposalList);
  7 +
  8 + /** @ngInject */
  9 + function proposalList() {
  10 +
  11 + /** @ngInject */
  12 + function ProposalListController(ArticleService, $scope, $element, $timeout, $log) {
  13 + $log.debug('ProposalListController');
  14 +
  15 + var vm = this;
  16 + vm.ArticleService = ArticleService;
  17 + vm.$scope = $scope;
  18 + vm.$element = $element;
  19 + vm.$timeout = $timeout;
  20 + vm.$log = $log;
  21 +
  22 + vm.init();
  23 +
  24 + vm.loadData();
  25 + }
  26 +
  27 + ProposalListController.prototype.init = function () {
  28 + // initial values
  29 + var vm = this;
  30 +
  31 + if(!vm.proposals){
  32 + throw { name: 'NotDefined', message: 'The attribute "proposals" is undefined.'};
  33 + }
  34 +
  35 + if(!vm.per_page){
  36 + vm.per_page = 5;
  37 + }
  38 +
  39 + vm.proposalsLength = vm.proposals.length;
  40 + };
  41 +
  42 + ProposalListController.prototype.loadData = function () {
  43 + // async values
  44 + var vm = this;
  45 +
  46 + // requeue to wait until DOM be created
  47 + vm.$timeout(function(){
  48 + attachPopover.call(vm);
  49 + }, 100);
  50 + };
  51 +
  52 + function attachPopover(){
  53 + var vm = this;
  54 +
  55 + vm.popover = angular.element(vm.$element.find('.btn-question'));
  56 + vm.popover.popover({
  57 + html: true,
  58 + placement: 'bottom',
  59 + animation: true,
  60 + title: 'Regra de posição das propostas',
  61 + content: '<p>É calculada pelo saldo de interações das propostas (curtidas - não curtidas) dividido pela diferença de exibições entre elas.</p><p>O objetivo dessa correção é compensar o saldo de interações e a diferença de exibições das propostas que não tiveram muitas oportunidades de visualização ou das propostas que tiveram mais oportunidades de visualização que a média.</p><p>Com essa correção, é possível comparar propostas que entraram em diferentes momentos, durante todo o período da consulta.</p>'
  62 + });
  63 + }
  64 +
  65 + var directive = {
  66 + restrict: 'E',
  67 + templateUrl: 'app/components/proposal-list/proposal-list.html',
  68 + scope: {
  69 + proposals: '=',
  70 + per_page: '='
  71 + },
  72 + controller: ProposalListController,
  73 + controllerAs: 'vm',
  74 + bindToController: true
  75 + };
  76 +
  77 +
  78 + return directive;
  79 + }
  80 +
  81 +})();
... ...
src/app/components/proposal-list/proposal-list.html 0 → 100644
... ... @@ -0,0 +1,45 @@
  1 +<div class="proposal-list">
  2 +
  3 + <div class="table-responsive" ng-if="vm.loading">
  4 + <div class="table-responsive">Carregando...</div>
  5 + </div>
  6 + <div class="table-responsive" ng-if="!vm.loading && vm.proposals">
  7 + <table class="table table-striped">
  8 + <thead>
  9 + <tr>
  10 + <th>
  11 + Colocação
  12 + <button type="button" class="btn btn-link btn-question">?</button>
  13 + </th>
  14 + <th>123 PROPOSTAS</th>
  15 + </tr>
  16 + </thead>
  17 + <tbody>
  18 + <tr ng-repeat="proposal in vm.proposals">
  19 +
  20 + <td class="color-theme-fg">
  21 + <span class="position">{{::($index+1)}}º</span>
  22 + </td>
  23 + <td>
  24 + <div class="row">
  25 + <div class="col-xs-12">
  26 + <span class="abstract">{{proposal.abstract}}</span>
  27 + </div>
  28 + </div>
  29 + <div class="row row-actions">
  30 + <div class="col-md-9">
  31 + <button type="button" class="btn btn-link btn-rate color-theme-common-fg">
  32 + Avalie esta proposta
  33 + <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
  34 + </button>
  35 + </div>
  36 + <div class="col-md-3">
  37 + <proposal-stats views="{{::proposal.hits}}" up="{{::proposal.votes_for}}" down="{{::proposal.votes_against}}"></proposal-stats>
  38 + </div>
  39 + </div>
  40 + </td>
  41 + </tr>
  42 + </tbody>
  43 + </table>
  44 + </div>
  45 +</div>
... ...
src/app/components/proposal-list/proposal-list.scss 0 → 100644
... ... @@ -0,0 +1,94 @@
  1 +.proposal-list {
  2 + table {
  3 + border-radius: 4px;
  4 + overflow: hidden;
  5 + }
  6 +
  7 + thead {
  8 + th{
  9 + color: #fff;
  10 + background-color: #606060;
  11 + &:first-child {
  12 + background-color: #484848;
  13 + text-align: right;
  14 + width: 160px;
  15 + padding-right: 20px;
  16 + }
  17 + }
  18 +
  19 + .btn-question {
  20 + color: #484848;
  21 + background-color: #fff;
  22 + display: inline-block;
  23 + text-align: center;
  24 + width: 22px;
  25 + height: 22px;
  26 + margin-left: 10px;
  27 + padding: 0;
  28 + border-radius: 100%;
  29 + }
  30 +
  31 + .popover {
  32 + color: #484848;
  33 + }
  34 + }
  35 +
  36 + tbody {
  37 + tr {
  38 + background-color: #fff;
  39 + }
  40 +
  41 + td {
  42 + &:first-child {
  43 + font-size: 22px;
  44 + font-weight: bold;
  45 + padding: 0;
  46 + text-align: right;
  47 + }
  48 + }
  49 +
  50 + .position {
  51 + display: block;
  52 + width: 100%;
  53 + margin: 20px 0;
  54 + padding: 5px 20px;
  55 + border-top-right-radius: 4px;
  56 + border-bottom-right-radius: 4px;
  57 + }
  58 + }
  59 +
  60 + .abstract {
  61 + color: #393939;
  62 + font-size: 18px;
  63 + }
  64 +
  65 + .row-actions {
  66 + padding-top: 20px;
  67 + }
  68 +
  69 + .btn-rate {
  70 + padding: 0;
  71 + font-size: 20px;
  72 + font-weight: bold;
  73 + text-decoration: blink;
  74 + }
  75 +
  76 + // override bootstrap
  77 + .table-striped > tbody > tr:nth-of-type(odd) {
  78 + // impar
  79 + background-color: #eaeaea;
  80 +
  81 + .position {
  82 + background-color: #fff;
  83 + }
  84 + }
  85 +
  86 + .table-striped > tbody > tr:nth-of-type(even) {
  87 + // par
  88 + background-color: #fff;
  89 +
  90 + .position {
  91 + background-color: #eaeaea;
  92 + }
  93 + }
  94 +}
... ...
src/app/components/proposal-ranking/proposal-ranking-carousel.scss
... ... @@ -1,90 +0,0 @@
1   -.proposal-ranking {
2   - // both: carousel and list
3   -}
4   -
5   -.proposal-ranking--carousel {
6   - background-color: #f1f1f1;
7   - border-radius: 5px;
8   - overflow: hidden;
9   -
10   - &-top {
11   - position: relative;
12   - color: #fff;
13   - font-weight: bold;
14   - font-size: 25px;
15   - padding: 20px 15px;
16   -
17   -
18   - &-triggers {
19   - position: absolute;
20   - right: 15px;
21   - top: 20px;
22   -
23   - button {
24   - border: 1px solid #fff;
25   - border-radius: 100%;
26   - width: 15px;
27   - height: 15px;
28   - margin-right: 5px;
29   - background-color: transparent;
30   -
31   - cursor: pointer;
32   -
33   - &.active {
34   - background-color: #fff;
35   - }
36   - }
37   - }
38   - }
39   -
40   - &-middle {
41   - position: relative;
42   - padding: 25px 30px;
43   - cursor: pointer;
44   - -webkit-user-select: none;
45   - -khtml-user-select: none;
46   - -moz-user-select: none;
47   - -ms-user-select: none;
48   - user-select: none;
49   -
50   - .content {
51   - position: relative;
52   - z-index: 2;
53   - }
54   -
55   - &-watermark {
56   - position: absolute;
57   - bottom: 1px;
58   - left: -5px;
59   - color: #ddd;
60   - font-size: 150px;
61   - font-weight: bold;
62   - line-height: 116px;
63   - z-index: 1;
64   -
65   - }
66   - }
67   -
68   - &-bottom {
69   - position: relative;
70   - color: #fff;
71   - padding: 10px;
72   - font-weight: bold;
73   - cursor: pointer;
74   - z-index: 10;
75   -
76   - &-icon {
77   - position: absolute;
78   - top: 10px;
79   - right: 15px;
80   -
81   - .glyphicon {
82   - position: relative;
83   - top: -2px;
84   - background-color: #fff;
85   - padding: 6px 5px 5px 6px;
86   - border-radius: 100%;
87   - }
88   - }
89   - }
90   -}
src/app/components/proposal-ranking/proposal-ranking-list.scss
... ... @@ -1,98 +0,0 @@
1   -.proposal-ranking {
2   - // both: carousel and list
3   -}
4   -
5   -.proposal-ranking--list {
6   - table {
7   - border-radius: 4px;
8   - overflow: hidden;
9   - }
10   -
11   - thead {
12   - th{
13   - color: #fff;
14   - background-color: #606060;
15   - &:first-child {
16   - background-color: #484848;
17   - text-align: right;
18   - width: 160px;
19   - padding-right: 20px;
20   - }
21   - }
22   -
23   - .btn-question {
24   - color: #484848;
25   - background-color: #fff;
26   - display: inline-block;
27   - text-align: center;
28   - width: 22px;
29   - height: 22px;
30   - margin-left: 10px;
31   - padding: 0;
32   - border-radius: 100%;
33   - }
34   -
35   - .popover {
36   - color: #484848;
37   - }
38   - }
39   -
40   - tbody {
41   - tr {
42   - background-color: #fff;
43   - }
44   -
45   - td {
46   - &:first-child {
47   - font-size: 22px;
48   - font-weight: bold;
49   - padding: 0;
50   - text-align: right;
51   - }
52   - }
53   -
54   - .position {
55   - display: block;
56   - width: 100%;
57   - margin: 20px 0;
58   - padding: 5px 20px;
59   - border-top-right-radius: 4px;
60   - border-bottom-right-radius: 4px;
61   - }
62   - }
63   -
64   - .abstract {
65   - color: #393939;
66   - font-size: 18px;
67   - }
68   -
69   - .row-actions {
70   - padding-top: 20px;
71   - }
72   -
73   - .btn-rate {
74   - padding: 0;
75   - font-size: 20px;
76   - font-weight: bold;
77   - text-decoration: blink;
78   - }
79   -
80   - // override bootstrap
81   - .table-striped > tbody > tr:nth-of-type(odd) {
82   - // impar
83   - background-color: #eaeaea;
84   -
85   - .position {
86   - background-color: #fff;
87   - }
88   - }
89   -
90   - .table-striped > tbody > tr:nth-of-type(even) {
91   - // par
92   - background-color: #fff;
93   -
94   - .position {
95   - background-color: #eaeaea;
96   - }
97   - }
98   -}
src/app/components/proposal-ranking/proposal-ranking.directive.js
... ... @@ -1,143 +0,0 @@
1   -(function() {
2   - 'use strict';
3   -
4   - angular
5   - .module('dialoga')
6   - .directive('proposalRanking', proposalRanking);
7   -
8   - /** @ngInject */
9   - function proposalRanking() {
10   -
11   - /** @ngInject */
12   - function ProposalRankingController(ArticleService, $scope, $element, $timeout, $log) {
13   - $log.debug('ProposalRankingController');
14   -
15   - var vm = this;
16   - vm.ArticleService = ArticleService;
17   - vm.$scope = $scope;
18   - vm.$element = $element;
19   - vm.$timeout = $timeout;
20   - vm.$log = $log;
21   -
22   - vm.init();
23   - }
24   -
25   - ProposalRankingController.prototype.init = function () {
26   - // initial values
27   - var vm = this;
28   -
29   - vm.activeIndex = 0;
30   - vm.loading = false;
31   -
32   - if(angular.isDefined(vm.limit) && angular.isString(vm.limit)){
33   - vm.limit = parseInt(vm.limit);
34   - }else{
35   - vm.limit = 3;
36   - }
37   -
38   - vm.loadData();
39   - };
40   -
41   - ProposalRankingController.prototype.loadData = function () {
42   - // async values
43   - var vm = this;
44   -
45   - vm.loading = true;
46   -
47   - // simulate delay
48   - vm.$timeout(function(){
49   - vm.loading = false;
50   -
51   - // Fake Data
52   - // vm.proposals = vm.ArticleService.getProposals();
53   - vm.proposals = [{
54   - id: 4159,
55   - abstract: 'Ut odio unde porro in. Aut fuga magni adipisci. Recusandae ipsum distinctio omnis ut illum.',
56   - effective_support: 0.1572052401746725,
57   - hits: 4159,
58   - votes_against: 3779,
59   - votes_for: 1780
60   - },{
61   - id: 935,
62   - abstract: 'Aut fuga magni adipisci. Recusandae ipsum distinctio omnis ut illum. Magni sunt ut molestiae.',
63   - effective_support: 0.1572052401746725,
64   - hits: 8602,
65   - votes_against: 7005,
66   - votes_for: 8728
67   - },{
68   - id: 1008,
69   - abstract: 'Recusandae ipsum distinctio omnis ut illum. Magni sunt ut molestiae. Aut fuga magni adipisci.',
70   - effective_support: 0.1572052401746725,
71   - hits: 9181,
72   - votes_against: 612,
73   - votes_for: 1786
74   - }];
75   -
76   - if(vm.display === 'list'){
77   - // wait until DOM be created
78   - vm.$timeout(function(){
79   - attachPopover.call(vm);
80   - }, 20);
81   - }
82   - }, 2000);
83   - };
84   -
85   - ProposalRankingController.prototype.swipeLeft = function () {
86   - var vm = this;
87   -
88   - vm.activeIndex = (vm.activeIndex < vm.limit - 1) ? ++vm.activeIndex : 0;
89   - };
90   -
91   - ProposalRankingController.prototype.swipeRight = function () {
92   - var vm = this;
93   -
94   - vm.activeIndex = (vm.activeIndex > 0) ? --vm.activeIndex : vm.limit - 1;
95   - };
96   -
97   - ProposalRankingController.prototype.switchProposal = function (index) {
98   - var vm = this;
99   -
100   - if(index >= 0 && index < vm.limit) {
101   - vm.activeIndex = index;
102   - }else{
103   - vm.$log.warn('[switchProposal] "index" not handled:', index);
104   - }
105   - };
106   -
107   - ProposalRankingController.prototype.showProposals = function () {
108   - var vm = this;
109   -
110   - // notify parents - handled by parents
111   - vm.$scope.$emit('see-proposals');
112   - };
113   -
114   - function attachPopover(){
115   - var vm = this;
116   -
117   - vm.popover = angular.element(vm.$element.find('.btn-question'));
118   - vm.popover.popover({
119   - html: true,
120   - placement: 'top',
121   - animation: true,
122   - title: 'Regra de posição das propostas',
123   - content: '<p>É calculada pelo saldo de interações das propostas (curtidas - não curtidas) dividido pela diferença de exibições entre elas.</p><p>O objetivo dessa correção é compensar o saldo de interações e a diferença de exibições das propostas que não tiveram muitas oportunidades de visualização ou das propostas que tiveram mais oportunidades de visualização que a média.</p><p>Com essa correção, é possível comparar propostas que entraram em diferentes momentos, durante todo o período da consulta.</p>'
124   - });
125   - }
126   -
127   - var directive = {
128   - restrict: 'E',
129   - templateUrl: 'app/components/proposal-ranking/proposal-ranking.html',
130   - scope: {
131   - limit: '&',
132   - display: '='
133   - },
134   - controller: ProposalRankingController,
135   - controllerAs: 'vm',
136   - bindToController: true
137   - };
138   -
139   -
140   - return directive;
141   - }
142   -
143   -})();
src/app/components/proposal-ranking/proposal-ranking.html
... ... @@ -1,84 +0,0 @@
1   -<div class="proposal-ranking contraste-box">
2   - <div class="proposal-ranking--carousel" ng-if="vm.display === 'carousel'">
3   - <div ng-if="!vm.proposals">
4   - Carregando...
5   - </div>
6   - <div ng-if="vm.proposals">
7   - <div class="proposal-ranking--carousel-top color-theme-bg">
8   - <div class="proposal-ranking--carousel-position" ng-repeat="proposal in vm.proposals">
9   - <span ng-show="vm.activeIndex === $index">{{::($index+1)}}º</span>
10   - <span ng-show="vm.activeIndex === $index">Lugar</span>
11   - </div>
12   - <div class="proposal-ranking--carousel-top-triggers" ng-if="vm.proposals">
13   - <ul class="list-inline">
14   - <li class="item-dot" ng-repeat="proposal in vm.proposals">
15   - <button type="button" ng-class="{'active': vm.activeIndex === $index}" ng-click="vm.switchProposal($index)" title="Apersentar proposta na posição {{$index}}"></button>
16   - </li>
17   - </ul>
18   - </div>
19   - </div>
20   - <div class="proposal-ranking--carousel-middle" ng-swipe-left="vm.swipeLeft()" ng-swipe-right="vm.swipeRight()">
21   - <div ng-repeat="proposal in vm.proposals" class="animation-swipe">
22   - <div class="content">
23   - <div ng-show="vm.activeIndex === $index">{{::proposal.abstract}}</div>
24   - </div>
25   - <div class="proposal-ranking--carousel-middle-watermark" ng-show="vm.activeIndex === $index">
26   - <span>{{::($index+1)}}º</span>
27   - </div>
28   - </div>
29   - </div>
30   - <div class="proposal-ranking--carousel-bottom color-theme-common-bg" ng-click="vm.showProposals()">
31   - <div>Veja as propostas mais vortadas</div>
32   - <div class="proposal-ranking--carousel-bottom-icon">
33   - <span class="glyphicon glyphicon-chevron-down pull-right color-theme-common-fg"></span>
34   - </div>
35   - </div>
36   - </div>
37   - </div>
38   -
39   - <div class="proposal-ranking--list" ng-if="vm.display==='list'">
40   -
41   - <div class="table-responsive" ng-if="vm.loading">
42   - <div class="table-responsive">Carregando...</div>
43   - </div>
44   - <div class="table-responsive" ng-if="!vm.loading && vm.proposals">
45   - <table class="table table-striped">
46   - <thead>
47   - <tr>
48   - <th>
49   - Colocação
50   - <button type="button" class="btn btn-link btn-question">?</button>
51   - </th>
52   - <th>123 PROPOSTAS</th>
53   - </tr>
54   - </thead>
55   - <tbody>
56   - <tr ng-repeat="proposal in vm.proposals">
57   -
58   - <td class="color-theme-fg">
59   - <span class="position">{{::($index+1)}}º</span>
60   - </td>
61   - <td>
62   - <div class="row">
63   - <div class="col-xs-12">
64   - <span class="abstract">{{proposal.abstract}}</span>
65   - </div>
66   - </div>
67   - <div class="row row-actions">
68   - <div class="col-md-9">
69   - <button type="button" class="btn btn-link btn-rate color-theme-common-fg">
70   - Avalie esta proposta
71   - <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
72   - </button>
73   - </div>
74   - <div class="col-md-3">
75   - <proposal-stats views="{{::proposal.hits}}" up="{{::proposal.votes_for}}" down="{{::proposal.votes_against}}"></proposal-stats>
76   - </div>
77   - </div>
78   - </td>
79   - </tr>
80   - </tbody>
81   - </table>
82   - </div>
83   - </div>
84   -</div>
src/app/components/topic-list/topic-list.directive.js 0 → 100644
... ... @@ -0,0 +1,316 @@
  1 +(function() {
  2 + 'use strict';
  3 +
  4 + angular
  5 + .module('dialoga')
  6 + .directive('topicList', topicList);
  7 +
  8 + /** @ngInject */
  9 + function topicList() {
  10 +
  11 + /** @ngInject */
  12 + function TopicListController($scope, $element, $location, $filter, $log) {
  13 + $log.debug('TopicListController');
  14 +
  15 + // alias
  16 + var vm = this;
  17 +
  18 + // dependencies
  19 + vm.$scope = $scope;
  20 + vm.$element = $element;
  21 + vm.$location = $location;
  22 + vm.$filter = $filter;
  23 + vm.$log = $log;
  24 + vm.defaultLimit = 6;
  25 +
  26 + // initialization
  27 + vm.init();
  28 + }
  29 +
  30 + TopicListController.prototype.init = function() {
  31 + var vm = this;
  32 +
  33 + if (!vm.article) {
  34 + vm.$log.warn('no article to display. Tip: use a ng-if before use this directive');
  35 + return;
  36 + }
  37 +
  38 + vm.categories = vm.article.categories;
  39 + vm.programs = vm.article.children;
  40 + vm.orderCriteries = [
  41 + { label: 'Título', name: 'titulo' },
  42 + { label: 'Tema', name: 'tema' },
  43 + { label: 'Aleatório', name: 'aleatorio' }
  44 + ];
  45 +
  46 + vm.filtredProgramList = vm.getFiltredPrograms();
  47 + vm.search = vm.$location.search();
  48 +
  49 + // Add initial values for the filter
  50 + vm.query = (vm.search && vm.search.filtro) ? vm.search.filtro : null;
  51 + vm.limitTo = (vm.search && vm.search.limite) ? parseInt(vm.search.limite, 10) : vm.defaultLimit;
  52 + vm.categoryFilter = (vm.search && vm.search.tema) ? vm.getCategoryBySlug(vm.search.tema) : null;
  53 + vm.orderCriteria = (vm.search && vm.search.ordem) ? { name: vm.search.ordem } : null;
  54 + vm.reverse = (vm.search && vm.search.reverso) ? true : false;
  55 +
  56 + if (!angular.equals({}, vm.search)) {
  57 + var $el = vm.$element;
  58 + angular.element('body').animate({scrollTop: $el.offset().top}, 'slow');
  59 + }
  60 +
  61 + // update window location params
  62 + vm.$scope.$watch('vm.query', function(newValue/*, oldValue*/) {
  63 + vm.search.filtro = newValue ? newValue : null;
  64 + vm.$location.search('filtro', vm.search.filtro);
  65 + if(vm.search.filtro){
  66 + vm.limitTo = vm.programs.length;
  67 + }else{
  68 + vm.limitTo = vm.defaultLimit;
  69 + }
  70 + vm.filtredProgramList = vm.getFiltredPrograms();
  71 + });
  72 +
  73 + vm.$scope.$watch('vm.limitTo', function(newValue/*, oldValue*/) {
  74 + vm.search.limite = (newValue && newValue !== vm.defaultLimit) ? newValue : null;
  75 + vm.$location.search('limite', vm.search.limite);
  76 + vm.filtredProgramList = vm.getFiltredPrograms();
  77 + });
  78 +
  79 + vm.$scope.$watch('vm.categoryFilter', function(newValue/*, oldValue*/) {
  80 + vm.search.tema = newValue ? newValue.slug : null;
  81 + vm.$location.search('tema', vm.search.tema);
  82 + if(vm.search.tema){
  83 + vm.limitTo = vm.programs.length;
  84 + }
  85 + vm.filtredProgramList = vm.getFiltredPrograms();
  86 + });
  87 +
  88 + vm.$scope.$watch('vm.orderCriteria', function(newValue/*, oldValue*/) {
  89 + vm.search.ordem = (newValue && newValue.name) ? newValue.name : null;
  90 + vm.$location.search('ordem', vm.search.ordem);
  91 + vm.filtredProgramList = vm.getFiltredPrograms();
  92 + });
  93 +
  94 + vm.$scope.$watch('vm.reverse', function(newValue/*, oldValue*/) {
  95 + vm.search.reverso = newValue ? newValue : null;
  96 + vm.$location.search('reverso', vm.search.reverso);
  97 + vm.filtredProgramList = vm.getFiltredPrograms();
  98 + });
  99 +
  100 + };
  101 +
  102 + TopicListController.prototype.resetFilterValues = function() {
  103 + var vm = this;
  104 +
  105 + vm.query = null;
  106 + vm.limitTo = vm.defaultLimit;
  107 + vm.categoryFilter = null;
  108 + vm.orderCriteria = null;
  109 + };
  110 +
  111 + TopicListController.prototype.getIconClasses = function(category) {
  112 + var vm = this;
  113 +
  114 + vm.$log.debug('[TODO] getIconClasses of category:', category);
  115 + return 'glyphicon glyphicon-exclamation-sign';
  116 + };
  117 +
  118 + TopicListController.prototype.getCategoryBySlug = function(categorySlug) {
  119 + var vm = this;
  120 + var result = null;
  121 +
  122 + angular.forEach(vm.categories, function(value/*, key*/) {
  123 + if (value.slug === categorySlug) {
  124 + result = value;
  125 + }
  126 + });
  127 +
  128 + return result;
  129 + };
  130 +
  131 + TopicListController.prototype.filterByCategory = function(category, $event) {
  132 + var vm = this;
  133 +
  134 + $event.stopPropagation();
  135 +
  136 + if (category !== vm.categoryFilter) {
  137 +
  138 + // selected new filter
  139 + vm.categoryFilter = category;
  140 + } else {
  141 + vm.categoryFilter = null;
  142 + }
  143 + };
  144 +
  145 + TopicListController.prototype.showAll = function($event) {
  146 + var vm = this;
  147 +
  148 + $event.stopPropagation();
  149 +
  150 + vm.resetFilterValues();
  151 + vm.limitTo = vm.programs.length;
  152 + };
  153 +
  154 + TopicListController.prototype.getFiltredPrograms = function() {
  155 + var vm = this;
  156 +
  157 + var input = vm.programs;
  158 + var output = input;
  159 + var query = vm.query;
  160 + var categoryFilter = vm.categoryFilter;
  161 + var orderCriteria = vm.orderCriteria ? vm.orderCriteria : { name : 'aleatorio'};
  162 + var filter = vm.$filter('filter');
  163 + var orderBy = vm.$filter('orderBy');
  164 + var limitTo = vm.$filter('limitTo');
  165 + var limit = vm.limitTo ? vm.limitTo : 4;
  166 +
  167 + if (categoryFilter) {
  168 + output = _filterByCategory(output, categoryFilter);
  169 + }
  170 +
  171 + if (query) {
  172 + output = filter(output, query, false);
  173 + }
  174 +
  175 + switch (orderCriteria.name) {
  176 + case 'titulo':
  177 + output = orderBy(output, 'title', vm.reverse);
  178 + break;
  179 + case 'tema':
  180 + output = orderBy(output, 'categories[0].name', vm.reverse);
  181 + break;
  182 + case 'more_participants':
  183 + vm.$log.info('Criteria not handled yet: ', orderCriteria);
  184 + break;
  185 + case 'aleatorio':
  186 + // shuffling
  187 + // if (!vm._isShuffled){
  188 + output = vm.filterShuffle(output);
  189 + // vm._isShuffled = true;
  190 + // }
  191 +
  192 + if (vm.reverse) {
  193 + output = output.slice().reverse();
  194 + }
  195 +
  196 + break;
  197 + default:
  198 + vm.$log.warn('Criteria not matched: ', orderCriteria);
  199 + break;
  200 + }
  201 +
  202 + output = limitTo(output, limit);
  203 +
  204 + return output;
  205 + };
  206 +
  207 + TopicListController.prototype.filterShuffle = function(input) {
  208 + var result = [];
  209 + var resultByCategory = {};
  210 +
  211 + // divide by categories
  212 + for (var i = 0; i < input.length; i++) {
  213 + var program = input[i];
  214 + var categorySlug = program.categories[0].slug;
  215 +
  216 + if (!resultByCategory[categorySlug]) {
  217 + resultByCategory[categorySlug] = [];
  218 + }
  219 +
  220 + resultByCategory[categorySlug].push(program);
  221 + }
  222 +
  223 + // shuffle each array
  224 + var prop = null;
  225 + var categoryWithPrograms = null;
  226 + for (prop in resultByCategory) {
  227 + if (resultByCategory.hasOwnProperty(prop)) {
  228 + categoryWithPrograms = resultByCategory[prop];
  229 + resultByCategory[prop] = shuffle(categoryWithPrograms);
  230 + }
  231 + }
  232 +
  233 + // Concat all into result array
  234 + // > while has program at Lists on resultByCategory
  235 + var hasProgram = true;
  236 + while (hasProgram) {
  237 +
  238 + var foundProgram = false;
  239 + // each categoryList with array of program
  240 + prop = null;
  241 + categoryWithPrograms = null;
  242 + for (prop in resultByCategory) {
  243 +
  244 + if (resultByCategory.hasOwnProperty(prop)) {
  245 + categoryWithPrograms = resultByCategory[prop];
  246 +
  247 + if (categoryWithPrograms.length > 0) {
  248 + var pivotProgram = categoryWithPrograms.pop();
  249 + result.push(pivotProgram);
  250 + foundProgram = true;
  251 + }
  252 + }
  253 + }
  254 +
  255 + if (!foundProgram) {
  256 + hasProgram = false;
  257 + }
  258 + }
  259 +
  260 + return result;
  261 + };
  262 +
  263 + var directive = {
  264 + restrict: 'E',
  265 + templateUrl: 'app/components/programas/programas.html',
  266 + scope: {
  267 + article: '='
  268 + },
  269 + controller: TopicListController,
  270 + controllerAs: 'vm',
  271 + bindToController: true
  272 + };
  273 +
  274 + return directive;
  275 + }
  276 +
  277 + function _filterByCategory (input, category) {
  278 + input = input || [];
  279 +
  280 + if (!category) {
  281 + // no filter
  282 + return input;
  283 + }
  284 +
  285 + var out = [];
  286 + for (var i = 0; i < input.length; i++) {
  287 + var program = input[i];
  288 + if (program.categories[0].slug === category.slug) {
  289 + out.push(program);
  290 + }
  291 + }
  292 +
  293 + return out;
  294 + }
  295 +
  296 + // -> Fisher–Yates shuffle algorithm
  297 + function shuffle (array) {
  298 + var currentIndex = array.length, temporaryValue, randomIndex ;
  299 +
  300 + // While there remain elements to shuffle...
  301 + while (0 !== currentIndex) {
  302 +
  303 + // Pick a remaining element...
  304 + randomIndex = Math.floor(Math.random() * currentIndex);
  305 + currentIndex -= 1;
  306 +
  307 + // And swap it with the current element.
  308 + temporaryValue = array[currentIndex];
  309 + array[currentIndex] = array[randomIndex];
  310 + array[randomIndex] = temporaryValue;
  311 + }
  312 +
  313 + return array;
  314 + }
  315 +
  316 +})();
... ...
src/app/components/topic-list/topic-list.directive.spec.js 0 → 100644
... ... @@ -0,0 +1,32 @@
  1 +(function() {
  2 + 'use strict';
  3 +
  4 + describe('topic directive', function() {
  5 + var compile, scope, directiveElem;
  6 +
  7 + beforeEach(function(){
  8 + module('dialoga');
  9 +
  10 + inject(function($compile, $rootScope){
  11 + compile = $compile;
  12 + scope = $rootScope.$new();
  13 + // mock topics
  14 + scope.topics = [];
  15 + });
  16 +
  17 + directiveElem = getCompiledElement();
  18 + });
  19 +
  20 + function getCompiledElement(){
  21 + var element = compile(angular.element('<topic-list></topic-list>'))(scope);
  22 + var compiledElement = compile(element)(scope);
  23 + scope.$digest();
  24 + return compiledElement;
  25 + }
  26 +
  27 + // it('ensure exist only one id "topic-list"', function() {});
  28 +
  29 + // it('should show default topics, one each category', function() {});
  30 +
  31 + });
  32 +})();
... ...
src/app/pages/programas/programa-content.controller.js
... ... @@ -18,28 +18,27 @@
18 18 vm.$log = $log;
19 19  
20 20 vm.init();
  21 + vm.loadData();
  22 + vm.attachListeners();
21 23 }
22 24  
23 25 ProgramaContentPageController.prototype.init = function() {
24 26 var vm = this;
25 27  
26   - var params = vm.$state.params;
27   -
28 28 vm.article = null;
29 29 vm.category = null;
30   - vm.loading = true;
31   - vm.error = false;
32   - vm.slug = params.slug;
33 30  
34   - vm.loadData();
35   - vm.attachListeners();
  31 + vm.error = false;
36 32 };
37 33  
38 34 ProgramaContentPageController.prototype.loadData = function() {
39 35 var vm = this;
40 36  
41   - // Get initial data of Program
42   - vm.DialogaService.getProgramBySlug(vm.slug, function(article) {
  37 + vm.loading = true;
  38 +
  39 + // Get program by slug
  40 + var slug = vm.$state.params.slug;
  41 + vm.DialogaService.getProgramBySlug(slug, function(article) {
43 42 vm.article = article;
44 43 vm.category = vm.article.categories[0];
45 44  
... ... @@ -54,41 +53,37 @@
54 53 };
55 54 }
56 55  
57   - // Get full data content of Program
58   - vm.loadContent();
59   -
  56 + vm.DialogaService.getProposalsByTopicId(vm.article.id, {}, function(data){
  57 + vm.proposals = data.children;
  58 + vm.proposalsTopRated = [
  59 + {abstract: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec tristique consectetur neque, at tincidunt enim volutpat sit amet. Integer sed cursus metus, non luctus risus. Mauris elementum est quis vehicula ullamcorper.'},
  60 + {abstract: 'Mauris elementum est quis vehicula ullamcorper. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec tristique consectetur neque, at tincidunt enim volutpat sit amet. Integer sed cursus metus, non luctus risus.'},
  61 + {abstract: 'Integer sed cursus metus, non luctus risus. Mauris elementum est quis vehicula ullamcorper. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec tristique consectetur neque, at tincidunt enim volutpat sit amet.'},
  62 + ];
  63 + }, function (error) {
  64 + vm.$log.error(error);
  65 + });
  66 +
  67 + vm.loading = false;
60 68 }, function(error) {
61 69 vm.$log.error(error);
62   - vm.$log.info('Rollback to home page.');
63   - vm.$state.go('inicio', {}, {location: true});
  70 + vm.error = error;
  71 + vm.loading = false;
  72 +
  73 + // vm.$log.info('Rollback to home page.');
  74 + // vm.$state.go('inicio', {}, {location: true});
64 75 });
  76 +
65 77 };
66 78  
67 79 ProgramaContentPageController.prototype.attachListeners = function() {
68 80 var vm = this;
69 81  
70   - vm.$scope.$on('see-proposals', function() {
  82 + vm.$scope.$on('proposal-carousel:toProposals', function() {
71 83 vm.$log.warn('TODO: handle see proposals / ranking');
72 84 });
73 85 };
74 86  
75   - // Get full data content of Program
76   - ProgramaContentPageController.prototype.loadContent = function() {
77   - var vm = this;
78   -
79   - vm.loading = true;
80   - if (!vm.article.body) {
81   - vm.DialogaService.getContentById(vm.article.id, function(data) {
82   - vm.article.body = data.article.body;
83   - vm.loading = false;
84   - }, function(error) {
85   - vm.loading = false;
86   - vm.error = error;
87   - });
88   - }
89   - vm.loading = false;
90   - };
91   -
92 87 ProgramaContentPageController.prototype.makeProposal = function() {
93 88 var vm = this;
94 89  
... ...
src/app/pages/programas/programa.html
... ... @@ -9,9 +9,11 @@
9 9 <div class="page--conheca-o-programa">
10 10 <section>
11 11 <div class="container">
12   - <div ng-if="!pageProgramaContent.article.body">
13   - <div ng-if="!pageProgramaContent.error" class="alert alert-info" role="alert">Carregando detalhes sobre o progama...</div>
14   - <div ng-if="pageProgramaContent.error" class="alert alert-warning" role="alert">{{pageProgramaContent}}</div>
  12 + <div class="row">
  13 + <div class="col-sm-12">
  14 + <div ng-if="!pageProgramaContent.article && pageProgramaContent.loading" class="alert alert-info" role="alert">Carregando detalhes sobre o progama...</div>
  15 + <div ng-if="!pageProgramaContent.article && pageProgramaContent.error" class="alert alert-warning" role="alert">Erro ao carregar o programa.</div>
  16 + </div>
15 17 </div>
16 18 </div>
17 19 </section>
... ... @@ -19,56 +21,59 @@
19 21 <div ng-if="pageProgramaContent.article.body" ng-class="pageProgramaContent.category.slug">
20 22 <section>
21 23 <div class="container">
22   - <article class="program-preview">
23   - <div class="row">
24   - <div class="col-md-12">
25   - <h1 class="program-preview--title color-theme-fg">{{::pageProgramaContent.article.title}}</h1>
26   - </div>
27   - <div class="col-md-8">
28   - <div class="program-preview--box contraste-box">
29   - <div class="program-preview--banner" ng-style="{'background-image':'url( {{::pageProgramaContent.banner.src}} )'}"></div>
30   - <div class="program-preview--box--content-wrapper">
31   - <div class="program-preview--icon icon-wrapper-rounded color-theme-bg" ng-class="pageProgramaContent.category.slug">
32   - <span class="icon" ng-class="'icon-tema-' + pageProgramaContent.category.slug"></span>
33   - </div>
34   - <div class="program-preview--abstract color-theme-fg">
35   - <h2>{{::stripHtml(pageProgramaContent.article.abstract)}}</h2>
36   - </div>
37   - <div class="program-preview--abstract-details">
38   - <p>Lorem ipsum dolor sit amet, ea veniam mucius ocurreret vix, ius ex nisl vidisse partiendo. Blandit nominavi cum ei, paulo quaestio his ei, eum minim salutandi in. Civibus albucius in quo, et eam posse facilisis. Debet suavitate sea ut, his ei feugiat fastidii eleifend. Quo ex quando maiestatis voluptatum, mel te perpetua maiestatis, sit ceteros legendos deserunt ea. Enim dolores moderatius eu pro, ad quo ignota aliquid meliore.</p>
39   - </div>
40   - <div class="program-preview--share">
41   - <ul class="list-inline">
42   - <li>Compartilhe este programa:</li>
43   - <li><social-share class="program-preview--share-directive"></social-share></li>
44   - </ul>
45   - </div>
46   - <div class="program-preview--make-proposal">
47   - <div class="row">
48   - <div class="col-sm-6">
49   - <div class="button--themed">
50   - <button type="button" class="btn btn-block" ng-click="pageProgramaContent.makeProposal()">Faça uma proposta</button>
  24 + <div class="row">
  25 + <article class="program-preview">
  26 +
  27 + <div class="col-md-12">
  28 + <h1 class="program-preview--title color-theme-fg">{{::pageProgramaContent.article.title}}</h1>
  29 + </div>
  30 + <div class="col-md-8">
  31 + <div class="program-preview--box contraste-box">
  32 + <div class="program-preview--banner" ng-style="{'background-image':'url( {{::pageProgramaContent.banner.src}} )'}"></div>
  33 + <div class="program-preview--box--content-wrapper">
  34 + <div class="program-preview--icon icon-wrapper-rounded color-theme-bg" ng-class="pageProgramaContent.category.slug">
  35 + <span class="icon" ng-class="'icon-tema-' + pageProgramaContent.category.slug"></span>
  36 + </div>
  37 + <div class="program-preview--abstract color-theme-fg">
  38 + <h2>{{::stripHtml(pageProgramaContent.article.abstract)}}</h2>
  39 + </div>
  40 + <div class="program-preview--abstract-details">
  41 + <p>Lorem ipsum dolor sit amet, ea veniam mucius ocurreret vix, ius ex nisl vidisse partiendo. Blandit nominavi cum ei, paulo quaestio his ei, eum minim salutandi in. Civibus albucius in quo, et eam posse facilisis. Debet suavitate sea ut, his ei feugiat fastidii eleifend. Quo ex quando maiestatis voluptatum, mel te perpetua maiestatis, sit ceteros legendos deserunt ea. Enim dolores moderatius eu pro, ad quo ignota aliquid meliore.</p>
  42 + </div>
  43 + <div class="program-preview--share">
  44 + <ul class="list-inline">
  45 + <li>Compartilhe este programa:</li>
  46 + <li><social-share class="program-preview--share-directive"></social-share></li>
  47 + </ul>
  48 + </div>
  49 + <div class="program-preview--make-proposal">
  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="pageProgramaContent.makeProposal()">Faça uma proposta</button>
  54 + </div>
51 55 </div>
52 56 </div>
53 57 </div>
54 58 </div>
55 59 </div>
56 60 </div>
57   - </div>
58   - <div class="col-md-4">
59   - <div class="row">
60   - <div class="col-xs-12">
61   - <h3 class="color-theme-fg">Propostas mais votadas</h3>
62   - <proposal-ranking limit="'3'" display="'carousel'"></proposal-ranking>
63   - </div>
64   - <div class="col-xs-12">
65   - <h3 class="color-theme-fg">Propostas nesse programa</h3>
66   - <proposal-related article="pageProgramaContent.article"></proposal-related>
  61 + <div class="col-md-4">
  62 + <div class="row">
  63 + <div class="col-xs-12">
  64 + <h3 class="color-theme-fg">Propostas mais votadas</h3>
  65 + <div ng-if="pageProgramaContent.proposalsTopRated">
  66 + <proposal-carousel proposals="pageProgramaContent.proposalsTopRated"></proposal-carousel>
  67 + </div>
  68 + </div>
  69 + <div class="col-xs-12">
  70 + <h3 class="color-theme-fg">Propostas nesse programa</h3>
  71 + <proposal-related article="pageProgramaContent.article"></proposal-related>
  72 + </div>
67 73 </div>
68 74 </div>
69   - </div>
70   - </div>
71   - </article>
  75 + </article>
  76 + </div>
72 77 </div>
73 78 </section>
74 79  
... ... @@ -77,16 +82,16 @@
77 82 <div class="proposal-ranking-section-header">
78 83 <h3 class="color-theme-fg">Resultados de propostas mais votadas</h3>
79 84 </div>
80   - <div class="proposal-ranking-section-table">
81   - <proposal-ranking limit="'5'" display="'list'"></proposal-ranking>
  85 + <div class="proposal-ranking-section-table" ng-if="pageProgramaContent.proposals">
  86 + <proposal-list proposals="pageProgramaContent.proposals"></proposal-list>
82 87 </div>
83 88 </div>
84 89 </section>
85   -
86   - <article class="program-content">
87   - <div class="row">
88   - <section ng-bind-html="pageProgramaContent.article.body"></section>
89   - </div>
90   - </article>
  90 +
  91 + <section class="section-content">
  92 + <article class="program-content" ng-if="pageProgramaContent.article">
  93 + <div ng-bind-html="pageProgramaContent.article.body"></div>
  94 + </article>
  95 + </section>
91 96 </div>
92 97 </div>
... ...
src/assets/images/icons/social-facebook.png

466 Bytes | W: | H:

606 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
src/assets/images/icons/social-flickr.png

812 Bytes | W: | H:

931 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
src/assets/images/icons/social-googleplus.png

1.09 KB | W: | H:

962 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
src/assets/images/icons/social-share.png

2.42 KB | W: | H:

1.36 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
src/assets/images/icons/social-twitter.png

974 Bytes | W: | H:

780 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
src/assets/images/icons/social-whatsapp.png

1.36 KB | W: | H:

1.09 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
src/assets/images/icons/social-youtube.png

672 Bytes | W: | H:

623 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
src/assets/images/icons/sprite.png

114 KB | W: | H:

105 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin