From ab0b9d321f847ed953a24a64c65d89601e262f39 Mon Sep 17 00:00:00 2001
From: "Eduardo L. Buratti" <elb09@c3sl.ufpr.br>
Date: Thu, 14 Nov 2013 14:07:21 -0200
Subject: [PATCH] web: Extend location filter in search ("outros..." option)

Signed-off-by: Eduardo L. Buratti <elb09@c3sl.ufpr.br>
---
 web/app/partials/search.filter.html | 36 +++++++++++++++
 web/app/partials/search.html        |  6 ++-
 web/assets/js/attendance.search.js  | 68 ++++++++++++++++++++++++++++-
 web/assets/less/main.less           | 27 ++++++++++++
 web/queries/get_cities_by_state.sql |  5 +++
 web/routes/cities.js                | 10 +++++
 web/routes/states.js                | 31 +++++++++++++
 web/server.js                       |  6 +++
 8 files changed, 186 insertions(+), 3 deletions(-)
 create mode 100644 web/app/partials/search.filter.html
 create mode 100644 web/queries/get_cities_by_state.sql
 create mode 100644 web/routes/cities.js
 create mode 100644 web/routes/states.js

diff --git a/web/app/partials/search.filter.html b/web/app/partials/search.filter.html
new file mode 100644
index 0000000..a8ba741
--- /dev/null
+++ b/web/app/partials/search.filter.html
@@ -0,0 +1,36 @@
+<div class="modal-backdrop fade in"></div>
+<div class="modal">
+    <div class="modal-dialog">
+        <div class="modal-content">
+            <form role="form" ng-submit="more.submit()">
+                <div class="modal-header">
+                    <h4 class="modal-title">Mais opções...</h4>
+                </div>
+                <div class="modal-body">
+                    <div class="well well-scrolled">
+                        <div class="tree-item" ng-repeat="item in more.items" >
+                            <div class="tree-item-title" ng-click="more.expand(item)">
+                                <small><i class="glyphicon" ng-class="{'glyphicon-plus': !item.expanded, 'glyphicon-minus': item.expanded}"></i></small>
+                                {{ item.title }}
+                            </div>
+                            <ul class="tree-item-subitems">
+                                <li ng-repeat="subitem in item.subitems" ng-show="item.expanded">
+                                    <div class="checkbox">
+                                        <label>
+                                            <input type="checkbox" ng-click="more.select(subitem)">
+                                            {{ subitem.title }}
+                                        </label>
+                                    </div>
+                                </li>
+                            </ul>
+                        </div>
+                    </div>
+                </div>
+                <div class="modal-footer">
+                    <button class="btn btn-default" ng-click="more.cancel()">Cancelar</button>
+                    <button type="submit" class="btn btn-primary">Adicionar</button>
+                </div>
+            </form>
+        </div>
+    </div>
+</div>
\ No newline at end of file
diff --git a/web/app/partials/search.html b/web/app/partials/search.html
index 504e326..f3ca24a 100644
--- a/web/app/partials/search.html
+++ b/web/app/partials/search.html
@@ -1,3 +1,5 @@
+<div ng-include="more.includeUrl"></div>
+
 <div class="container">
     <div class="search-filters well">
         <ul ng-repeat="filter in filters">
@@ -5,7 +7,7 @@
             <li class="checkbox" ng-repeat="opt in filter.options">
                 <label><input type="checkbox" ng-model="opt.value" ng-click="updateFilters(filter, opt)"> {{ opt.title }}</label>
             </li>
-            <li class="checkbox" ng-show="filter.more"><a href="">outros...</a></li>
+            <li class="checkbox" ng-show="filter.more"><a href="" ng-click="more.show()">outros...</a></li>
         </ul>
 
         <div class="clearfix"></div>
@@ -32,7 +34,7 @@
             <tr ng-repeat="point in points">
                 <td><a href="">{{ point.name }}</a></td>
                 <td>{{ point.location }}</a></td>
-                <td>{{ point.project }} </a></td>
+                <td>{{ point.project }}</a></td>
             </tr>
         </tbody>
     </table>
diff --git a/web/assets/js/attendance.search.js b/web/assets/js/attendance.search.js
index d88470f..3fa4744 100644
--- a/web/assets/js/attendance.search.js
+++ b/web/assets/js/attendance.search.js
@@ -66,7 +66,15 @@ angular.module('datasid.attendance.search', []).
         });
     }).
 
-    controller('SearchCtrl', function ($scope, $rootScope, PointsFactory) {
+    factory('CityFactory', function($resource) {
+        return $resource('/api/cities/:state');
+    }).
+
+    factory('StateFactory', function($resource) {
+        return $resource('/api/states');
+    }).
+
+    controller('SearchCtrl', function ($scope, $rootScope, PointsFactory, CityFactory, StateFactory) {
         $scope.points = [];
 
         $scope.sorting = 'name';
@@ -160,6 +168,64 @@ angular.module('datasid.attendance.search', []).
             });
         };
 
+        $scope.more = {
+            includeUrl: '',
+            items: [ ],
+            selectedItems: [],
+            show: function () {
+                this.includeUrl = 'partials/search.filter.html';
+
+                StateFactory.query(function (states) {
+                    angular.forEach(states, function (state) {
+                        $scope.more.items.push({
+                            key: state.key,
+                            title: state.title,
+                            expanded: false,
+                            subitems: []
+                        });
+                    });
+                });
+            },
+            cancel: function () {
+                this.includeUrl = '';
+                $scope.more.items = [];
+            },
+            submit: function () {
+                for (var i=0; i < this.selectedItems.length; i++)
+                    $scope.filters[1].options.push(this.selectedItems[i]);
+
+                $scope.updateFilters();
+
+                this.includeUrl = '';
+                $scope.more.items = [];
+            },
+            expand: function (item) {
+                if (item.expanded) {
+                    item.expanded = false;
+                    return;
+                }
+
+                if (item.subitems.length == 0)
+                    item.subitems = CityFactory.query({state: item.key});
+
+                item.expanded = true;
+            },
+            select: function (item) {
+                item.value = !(item.value || false);
+
+                if (item.value)
+                    this.selectedItems.push(item);
+                else {
+                    for (var i=0; i < this.selectedItems.length; i++) {
+                        if (this.selectedItems[i] == item) {
+                            this.selectedItems.splice(i, 1);
+                            break;
+                        }
+                    }
+                }
+            }
+        };
+
         $scope.updateFilters = function (filterIn, optionIn) {
             $scope.compiledFilters = {};
 
diff --git a/web/assets/less/main.less b/web/assets/less/main.less
index 100593d..4a22f76 100644
--- a/web/assets/less/main.less
+++ b/web/assets/less/main.less
@@ -149,6 +149,7 @@ p.note {
 
 .modal {
     display: block;
+    overflow: hidden;
 }
 
 .saving {
@@ -158,4 +159,30 @@ p.note {
   margin: 0px -4px -3px 4px;
   background-image: url("../img/saving.gif");
   background-repeat: no-repeat;
+}
+
+.tree-item {
+    border-bottom: 1px solid #e5e5e5;
+
+    .tree-item-title {
+        padding: 6px 2px;
+        cursor: pointer;
+        font-size: 1.2em;
+    }
+
+    .tree-item-subitems {
+        list-style: none;
+        padding-left: 12px;
+
+        .checkbox {
+            margin-top: 3px;
+            margin-bottom: 3px;
+        }
+    }
+}
+
+.well-scrolled {
+    height: 300px;
+    overflow-x: auto;
+    padding-top: 6px;
 }
\ No newline at end of file
diff --git a/web/queries/get_cities_by_state.sql b/web/queries/get_cities_by_state.sql
new file mode 100644
index 0000000..ba6220e
--- /dev/null
+++ b/web/queries/get_cities_by_state.sql
@@ -0,0 +1,5 @@
+SELECT
+    id AS key,
+    INITCAP(name) || ', ' || UPPER(state) AS title
+FROM city
+WHERE state = $1;
\ No newline at end of file
diff --git a/web/routes/cities.js b/web/routes/cities.js
new file mode 100644
index 0000000..cd1d17f
--- /dev/null
+++ b/web/routes/cities.js
@@ -0,0 +1,10 @@
+exports.list = function(req, res) {
+    var state = req.params.state;
+
+    var query = __dirname + "/../queries/get_cities_by_state.sql";
+
+    req.db.queryFromFile(query, [state], function(result) {
+        req.db.done();
+        res.json(result.rows);
+    });
+};
\ No newline at end of file
diff --git a/web/routes/states.js b/web/routes/states.js
new file mode 100644
index 0000000..8c7edfb
--- /dev/null
+++ b/web/routes/states.js
@@ -0,0 +1,31 @@
+exports.list = function(req, res) {
+    res.json([
+        { key: 'AC', title: 'Acre' },
+        { key: 'AL', title: 'Alagoas' },
+        { key: 'AP', title: 'Amapá' },
+        { key: 'AM', title: 'Amazonas' },
+        { key: 'BA', title: 'Bahia' },
+        { key: 'CE', title: 'Ceará' },
+        { key: 'DF', title: 'Distrito Federal' },
+        { key: 'ES', title: 'Espírito Santo' },
+        { key: 'GO', title: 'Goiás' },
+        { key: 'MA', title: 'Maranhão' },
+        { key: 'MT', title: 'Mato Grosso' },
+        { key: 'MS', title: 'Mato Grosso do Sul' },
+        { key: 'MG', title: 'Minas Gerais' },
+        { key: 'PA', title: 'Pará' },
+        { key: 'PB', title: 'Paraíba' },
+        { key: 'PR', title: 'Paraná' },
+        { key: 'PE', title: 'Pernambuco' },
+        { key: 'PI', title: 'Piauí' },
+        { key: 'RJ', title: 'Rio de Janeiro' },
+        { key: 'RN', title: 'Rio Grande do Norte' },
+        { key: 'RS', title: 'Rio Grande do Sul' },
+        { key: 'RO', title: 'Rondônia' },
+        { key: 'RR', title: 'Roraima' },
+        { key: 'SC', title: 'Santa Catarina' },
+        { key: 'SP', title: 'São Paulo' },
+        { key: 'SE', title: 'Sergipe' },
+        { key: 'TO', title: 'Tocantins' }
+    ]);
+};
\ No newline at end of file
diff --git a/web/server.js b/web/server.js
index 1beb80f..a85a697 100755
--- a/web/server.js
+++ b/web/server.js
@@ -6,6 +6,8 @@ var db = require('./middleware/db.js');
 var SQLiteStore = require('connect-sqlite3')(express);
 
 var sessions = require('./routes/sessions.js');
+var cities = require('./routes/cities.js');
+var states = require('./routes/states.js');
 var points = require('./routes/points.js');
 var telecenters = require('./routes/telecenters.js');
 var charts = require('./routes/charts.js');
@@ -29,6 +31,10 @@ app.post('/api/s', sessions.login);
 app.get('/api/s', sessions.get);
 app.delete('/api/s', sessions.logout);
 
+app.get('/api/states', db.connect, states.list);
+
+app.get('/api/cities/:state', db.connect, cities.list);
+
 app.all('/api/points', db.connect, points.list);
 app.all('/api/points/count', db.connect, points.count);
 
-- 
GitLab