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:
@@ -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;
|
||||
}
|
||||
@@ -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">
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user