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,7 +26,6 @@
26 26
27 // initialization 27 // initialization
28 vm.init(); 28 vm.init();
29 - vm.loadData();  
30 vm.attachListeners(); 29 vm.attachListeners();
31 } 30 }
32 31
@@ -35,10 +34,6 @@ @@ -35,10 +34,6 @@
35 // vm.programs = null; // scope var 34 // vm.programs = null; // scope var
36 }; 35 };
37 36
38 - ArticleGridController.prototype.loadData = function() {  
39 - // var vm = this;  
40 - };  
41 -  
42 ArticleGridController.prototype.attachListeners = function() { 37 ArticleGridController.prototype.attachListeners = function() {
43 // var vm = this; 38 // var vm = this;
44 }; 39 };
src/app/components/article-service/article.service.js
@@ -17,6 +17,8 @@ @@ -17,6 +17,8 @@
17 getCategoryBySlug: getCategoryBySlug, 17 getCategoryBySlug: getCategoryBySlug,
18 getTopics: getTopics, 18 getTopics: getTopics,
19 getTopicById: getTopicById, 19 getTopicById: getTopicById,
  20 + getProposals: getProposals,
  21 + getProposalsByTopicId: getProposalsByTopicId,
20 searchTopics: searchTopics, 22 searchTopics: searchTopics,
21 searchProposals: searchProposals 23 searchProposals: searchProposals
22 }; 24 };
@@ -90,6 +92,40 @@ @@ -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 function searchTopics (params, cbSuccess, cbError) { 129 function searchTopics (params, cbSuccess, cbError) {
94 // Ex.: /api/v1/search/article?type=ProposalsDiscussionPlugin::Topic&query=cisternas 130 // Ex.: /api/v1/search/article?type=ProposalsDiscussionPlugin::Topic&query=cisternas
95 var url = '/api/v1/search/article'; 131 var url = '/api/v1/search/article';
src/app/components/dialoga-service/dialoga.service.js
@@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
17 getProgramBySlug: getProgramBySlug, 17 getProgramBySlug: getProgramBySlug,
18 getProgramsRandom: getProgramsRandom, 18 getProgramsRandom: getProgramsRandom,
19 getProposals: getProposals, 19 getProposals: getProposals,
  20 + getProposalsByTopicId: getProposalsByTopicId,
20 getEvents: getEvents, 21 getEvents: getEvents,
21 getQuestions: getQuestions, 22 getQuestions: getQuestions,
22 searchProgram: searchProgram, 23 searchProgram: searchProgram,
@@ -131,18 +132,20 @@ @@ -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 cbSuccess(CACHE.proposals); 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 function getEvents (cbSuccess, cbError) { 151 function getEvents (cbSuccess, cbError) {
src/app/components/programas/programas.directive.js
@@ -1,316 +0,0 @@ @@ -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,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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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,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,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,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,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 @@ @@ -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 @@ @@ -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,28 +18,27 @@
18 vm.$log = $log; 18 vm.$log = $log;
19 19
20 vm.init(); 20 vm.init();
  21 + vm.loadData();
  22 + vm.attachListeners();
21 } 23 }
22 24
23 ProgramaContentPageController.prototype.init = function() { 25 ProgramaContentPageController.prototype.init = function() {
24 var vm = this; 26 var vm = this;
25 27
26 - var params = vm.$state.params;  
27 -  
28 vm.article = null; 28 vm.article = null;
29 vm.category = null; 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 ProgramaContentPageController.prototype.loadData = function() { 34 ProgramaContentPageController.prototype.loadData = function() {
39 var vm = this; 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 vm.article = article; 42 vm.article = article;
44 vm.category = vm.article.categories[0]; 43 vm.category = vm.article.categories[0];
45 44
@@ -54,41 +53,37 @@ @@ -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 }, function(error) { 68 }, function(error) {
61 vm.$log.error(error); 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 ProgramaContentPageController.prototype.attachListeners = function() { 79 ProgramaContentPageController.prototype.attachListeners = function() {
68 var vm = this; 80 var vm = this;
69 81
70 - vm.$scope.$on('see-proposals', function() { 82 + vm.$scope.$on('proposal-carousel:toProposals', function() {
71 vm.$log.warn('TODO: handle see proposals / ranking'); 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 ProgramaContentPageController.prototype.makeProposal = function() { 87 ProgramaContentPageController.prototype.makeProposal = function() {
93 var vm = this; 88 var vm = this;
94 89
src/app/pages/programas/programa.html
@@ -9,9 +9,11 @@ @@ -9,9 +9,11 @@
9 <div class="page--conheca-o-programa"> 9 <div class="page--conheca-o-programa">
10 <section> 10 <section>
11 <div class="container"> 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 </div> 17 </div>
16 </div> 18 </div>
17 </section> 19 </section>
@@ -19,56 +21,59 @@ @@ -19,56 +21,59 @@
19 <div ng-if="pageProgramaContent.article.body" ng-class="pageProgramaContent.category.slug"> 21 <div ng-if="pageProgramaContent.article.body" ng-class="pageProgramaContent.category.slug">
20 <section> 22 <section>
21 <div class="container"> 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 </div> 55 </div>
52 </div> 56 </div>
53 </div> 57 </div>
54 </div> 58 </div>
55 </div> 59 </div>
56 </div> 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 </div> 73 </div>
68 </div> 74 </div>
69 - </div>  
70 - </div>  
71 - </article> 75 + </article>
  76 + </div>
72 </div> 77 </div>
73 </section> 78 </section>
74 79
@@ -77,16 +82,16 @@ @@ -77,16 +82,16 @@
77 <div class="proposal-ranking-section-header"> 82 <div class="proposal-ranking-section-header">
78 <h3 class="color-theme-fg">Resultados de propostas mais votadas</h3> 83 <h3 class="color-theme-fg">Resultados de propostas mais votadas</h3>
79 </div> 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 </div> 87 </div>
83 </div> 88 </div>
84 </section> 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 </div> 96 </div>
92 </div> 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