Add search bar and filter options to member table

In addition to new functionality, the styles of the page were refined.
Resolves #19. Resolves #20.
This commit is contained in:
Jan Tuomi
2017-01-19 01:43:12 +02:00
parent 5585b52ebd
commit c6a2ed38b1
3 changed files with 272 additions and 102 deletions
+46
View File
@@ -0,0 +1,46 @@
#download-csv {
margin-left: 20px;
}
.content-area {
padding-left: 15px;
padding-right: 15px;
}
.content-area-title {
text-align: left;
float: left;
}
.filter-field {
margin-left: 5px;
margin-right: 5px;
}
.table-button {
margin-left: 5px;
margin-right: 5px;
margin-bottom: 5px;
}
.table-button-column {
text-align: right;
}
.filters {
text-align: right;
float: right;
margin-bottom: 5px;
}
.first-filter {
margin-top: 20px;
}
.last-filter {
margin-bottom: 30px;
}
.inline-title {
display: inline;
}
+45 -33
View File
@@ -1,17 +1,33 @@
<link rel="stylesheet" href="/static/css/jasenlista.css">
<div class="row">
<div class="col-md-7">
<h3> Jäsenlista (jäseniä {{members.length}})</h3>
</div>
<div class="col-md-3">
<md-datepicker ng-model="datePicker" md-placeholder="Filter by date"></md-datepicker>
</div>
<div class="col-md-1 offset-md-1"> <!-- style="vertical-align: bottom;"-->
<input type="button" value="Clear filter" class="btn btn-success" ng-click="clear_filter()" />
</div>
</div>
<div>
<input type="button" value="Lataa CSV" class="btn btn-info" ng-click="loadCSV()"/>
<table id="choose-address-table" class="table table-striped">
<div class="row content-area">
<div class="form-inline col-md-12 filters first-filter">
<div class="form-group">
<label for="addedBeforeDatePicker"><h4>Lisätty</h4></label>
<md-datepicker class="filter-field" id="addedAfterDatePicker" ng-model="addedAfterDatePicker" md-placeholder="Lisätty jälkeen"></md-datepicker>
<md-datepicker class="filter-field" id="addedBeforeDatePicker" ng-model="addedBeforeDatePicker" md-placeholder="Lisätty ennen"></md-datepicker>
</div>
</div>
<div class="form-inline col-md-12 filters">
<div class="form-group">
<label for="paidBeforeDatePicker"><h4>Maksettu</h4></label>
<md-datepicker class="filter-field" id="paidAfterDatePicker" ng-model="paidAfterDatePicker" md-placeholder="Maksettu jälkeen"></md-datepicker>
<md-datepicker class="filter-field" id="paidBeforeDatePicker" ng-model="paidBeforeDatePicker" md-placeholder="Maksettu ennen"></md-datepicker>
</div>
</div>
<div class="form-inline col-md-12 last-filter">
<div class="form-group filters">
<input class="filter-field form-control" type="text" id="searchFilter" placeholder="Haku" ng-model="searchFilter"></input>
<input type="button" value="Suodata" class="filter-field btn btn-success" ng-click="doFilter()" />
<input type="button" value="Nollaa" class="filter-field btn btn-warning" ng-click="clearFilter()" />
</div>
<div class="content-area-title form-group">
<h3 class="inline-title">Jäsenlista (jäseniä: {{members.length}})</h3>
<input type="button" value="Lataa CSV" id="download-csv" class="btn btn-info" ng-click="loadCSV()"/>
</div>
</div>
<table id="choose-address-table" class="table table-striped">
<thead>
<tr class="ui-widget-header">
<th>Sukunimi</th>
@@ -24,27 +40,23 @@
<th>Maksanut</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="x in shown_members | orderBy: 'last_name'">
<td>{{ x.last_name }}</td>
<td>{{ x.first_name }}</td>
<td>{{ x.email }}</td>
<td>{{ x.AYY }}</td>
<td>{{ x.jas }}</td>
<td>{{ x.POR }}</td>
<td>{{ x.created }}</td>
<td>{{ x.paid }}</td>
<td>
<input type="button" value="Päivitä maksu" class="btn btn-success" ng-click="updatePayment(x.id)" />
</td>
<td>
<a href="#/edit/{{x.id}}"<input type="button" value="Edit" class="btn btn-info">Muokkaa</input></a>
</td>
<td>
<input type="button" value="Poista" class="btn btn-danger" confirmed-click="delete_member(x.id)" ng-confirm-click="Are you sure?"/>
</td>
</tr>
</tbody>
<tbody>
<tr ng-repeat="x in shown_members | orderBy: 'last_name'">
<td>{{ x.last_name }}</td>
<td>{{ x.first_name }}</td>
<td>{{ x.email }}</td>
<td>{{ x.AYY }}</td>
<td>{{ x.jas }}</td>
<td>{{ x.POR }}</td>
<td>{{ x.created }}</td>
<td>{{ x.paid }}</td>
<td class="table-button-column">
<input type="button" value="Päivitä maksu" class="table-button btn btn-success" ng-click="updatePayment(x.id)" />
<a href="#/edit/{{x.id}}"<input type="button" value="Edit" class="table-button btn btn-info">Muokkaa</input></a>
<input type="button" value="Poista" class="table-button btn btn-danger" confirmed-click="delete_member(x.id)" ng-confirm-click="Are you sure?"/>
</td>
</tr>
</tbody>
</table>
<div class="row" id="tommy">
<div class="col-sm-4">
+181 -69
View File
@@ -16,12 +16,12 @@ var notySuccess = notyfication('success',2500);
function editor(returnpath){
return function($scope, $http, $route, $routeParams, $window, $location) {
$scope.member = {"id": $routeParams.id};
$http.get("/members/api/member/"+$scope.member.id).then(function(response){
$http.get("/members/api/member/" + $scope.member.id).then(function(response) {
$scope.member = response.data;
});
$scope.send = function() {
$http.put("/members/api/member/"+$scope.member.id, $scope.member).then(function(data){
$http.put("/members/api/member/" + $scope.member.id, $scope.member).then( function(data){
notySuccess("Jäsentiedot tallennettu");
$location.path(returnpath);
});
@@ -31,101 +31,213 @@ function editor(returnpath){
}
}
}
app.directive('ngConfirmClick',
[
function()
{ return {
link: function (scope, element, attr)
{
app.directive('ngConfirmClick', [ function() { return {
link: function (scope, element, attr) {
var clickAction = attr.confirmedClick;
element.bind('click',function (event)
{
noty({
element.bind('click',function (event) {
noty( {
text: 'Oletko aivan varma? T. Lasse Lehtinen',
layout: 'bottomRight',
buttons: [
{
addClass: 'btn btn-danger', text: 'Kyllä', onClick: function($noty) {
// this = button element
// $noty = $noty element
$noty.close();
scope.$eval(clickAction)
}
},
{
addClass: 'btn btn-primary', text: 'Ei', onClick: function($noty) {
$noty.close();
}
}
]
});
buttons: [ {
addClass: 'btn btn-danger', text: 'Kyllä', onClick: function($noty) {
// this = button element
// $noty = $noty element
$noty.close();
scope.$eval(clickAction)
}
},
{
addClass: 'btn btn-primary', text: 'Ei', onClick: function($noty) {
$noty.close();
}
} ]
} );
});
}
}}]);
// controllers
app.controller("getController", function($scope, $http, $window, $location){
app.controller("getController", function($scope, $document, $http, $window, $location){
/* List of all members that are fetched from the database */
$scope.members = [];
$scope.getFunction = function() {
$http.get("/members/api/members").then(function(response){
$scope.members = response.data;
// map trues and falses to more user-friendly format
_.each($scope.members, function(m){
m.jas = m.jas ? "Kyllä" : "Ei";
m.AYY = m.AYY ? "Kyllä" : "Ei";
});
$scope.shown_members = $scope.members;
});
};
$scope.getFunction();
$scope.updatePayment= function(id){
$http.put("/members/api/member/"+id,{paid:moment().format("YYYY-MM-DD kk:mm:ss") }).then(function(resp){
$scope.getFunction();
/* Fetch all members from the database and show all members in the table */
$scope.updateMembers = function() {
$http.get("/members/api/members").then(function(response){
$scope.members = response.data;
// map trues and falses to more user-friendly format
_.each($scope.members, function(m){
m.jas = m.jas ? "Kyllä" : "Ei";
m.AYY = m.AYY ? "Kyllä" : "Ei";
});
$scope.shown_members = $scope.members;
});
};
/* Fetch a single member from the database by id and update its row */
$scope.updateMember = function(id) {
$http.get("/members/api/member/" + id).then(function(response) {
for (var i = 0; i < $scope.shown_members.length; i++) {
var member = $scope.shown_members[i];
if (String(member.id) == String(id)) {
member = response.data;
member.jas = member.jas ? "Kyllä" : "Ei";
member.AYY = member.AYY ? "Kyllä" : "Ei";
$scope.shown_members[i] = member;
}
}
});
};
/* Update the payment date of a single member to the current time and send
* the member to the database */
$scope.updatePayment= function(id){
$http.put("/members/api/member/"+id, { paid: moment().format("YYYY-MM-DD kk:mm:ss") }).then(function(resp) {
$scope.updateMember(id);
notySuccess("Maksupäivämäärä päivitetty.");
});
};
/* Redirect the browser to the CSV dump download endpoint */
$scope.loadCSV = function() {
window.location = "/members/api/getCSV";
};
$scope.delete_member = function(id) {
/* Delete a single member by id */
$scope.deleteMember = function(id) {
$http.delete("/members/api/member/" + id).then(
function(response) {
notySuccess("Poistaminen onnistui")
$scope.getFunction();
$scope.updateMembers();
},
function(response) {
notyError("Epäonnistui. Yritä uudelleen.");
$scope.getFunction();
$scope.updateMembers();
}
);
};
$scope.datePicker = null;
$scope.filter_by_date = function() {
if ($scope.datePicker == null)
{
$scope.shown_members = $scope.members;
}
else
{
$scope.shown_members = [];
for (var i = 0; i < $scope.members.length; i++)
{
if (moment($scope.members[i].paid) < $scope.datePicker)
{
$scope.shown_members.push($scope.members[i]);
/* Filter in only those members whose 'created' field comes
* before the specified date */
$scope.filterByAddedBeforeDate = function(members) {
if ($scope.addedBeforeDatePicker == null) {
return members;
}
}
}
};
$scope.clear_filter = function() {
$scope.datePicker = null;
$scope.getFunction();
var result = [];
for (var i = 0; i < members.length; i++) {
if (moment(members[i].created) <= $scope.addedBeforeDatePicker) {
result.push(members[i]);
}
}
return result;
};
/* Filter in only those members whose 'created' field comes
* after the specified date */
$scope.filterByAddedAfterDate = function(members) {
if ($scope.addedAfterDatePicker == null) {
return members;
}
var result = [];
for (var i = 0; i < members.length; i++) {
if (moment(members[i].created) > $scope.addedAfterDatePicker) {
result.push(members[i]);
}
}
return result;
};
/* Filter in only those members whose 'paid' field comes
* before the specified date */
$scope.filterByPaidBeforeDate = function(members) {
if ($scope.paidBeforeDatePicker == null) {
return members;
}
var result = [];
for (var i = 0; i < members.length; i++) {
if (moment(members[i].paid) <= $scope.paidBeforeDatePicker) {
result.push(members[i]);
}
}
return result;
};
/* Filter in only those members whose 'paid' field comes
* after the specified date */
$scope.filterByPaidAfterDate = function(members) {
if ($scope.paidAfterDatePicker == null) {
return members;
}
var result = [];
for (var i = 0; i < members.length; i++) {
if (moment(members[i].paid) > $scope.paidAfterDatePicker) {
result.push(members[i]);
}
}
return result;
};
/* Do a lazy search on the first name, last name and email fields
* If at least one of the aforementioned fields contains any of the search terms
* the search will be positive */
$scope.filterBySearch = function(members) {
if ($scope.searchFilter == null) {
return members;
}
var filterSearch = $scope.searchFilter.trim();
if (filterSearch.length == 0) {
return members;
}
var names = filterSearch.split(" ");
var result = [];
for (var i = 0; i < members.length; i++) {
var member = members[i];
for (var j = 0; j < names.length; j++) {
var name = names[j].trim();
if (name.length == 0) continue;
if (member.first_name.includes(name) || member.last_name.includes(name) || member.email.includes(name)) {
result.push(member);
break;
}
}
}
return result;
}
/* Run all filters on the members list */
$scope.doFilter = function() {
var result = $scope.members;
result = $scope.filterByAddedBeforeDate(result);
result = $scope.filterByAddedAfterDate(result);
result = $scope.filterByPaidBeforeDate(result);
result = $scope.filterByPaidAfterDate(result);
result = $scope.filterBySearch(result);
$scope.shown_members = result;
}
/* Clear all filter fields and reset the table view */
$scope.clearFilter = function() {
$scope.paidBeforeDatePicker = null;
$scope.paidAfterDatePicker = null;
$scope.addedBeforeDatePicker = null;
$scope.addedAfterDatePicker = null;
$scope.searchFilter = null;
$scope.updateMembers();
};
$scope.$watch('datePicker', function(newValue, oldValue) {
$scope.filter_by_date();
});
/* Start by resetting the whole thing */
$scope.clearFilter();
});
app.controller("postController", function($scope, $http, $location) {