Added sorting to user list, ASCending and DESCending on first and last name

fixed pagination where it wasnt rounding up so <x.5 would show not 1 page less than required.
fix issue introduced in last branch where group drop down/filter wouldnt work.
#51
This commit is contained in:
chris.watts90@outlook.com 2020-02-25 14:48:49 +00:00
parent 1e633e97c7
commit 20bf968675
9 changed files with 227 additions and 101 deletions

View File

@ -12,7 +12,7 @@ namespace Interfaces
/// Returns <see cref="UserList"/> with full list of users, /// Returns <see cref="UserList"/> with full list of users,
/// plus a total user count. Pagination options are supported. /// plus a total user count. Pagination options are supported.
/// </returns> /// </returns>
UserList GetUsers(int pageNumber = -1, int pageSize = -1, int groupId = -1); UserList GetUsers(int pageNumber = -1, int pageSize = -1, int groupId = -1, SortOptions sort = SortOptions.None);
/// <summary> /// <summary>
/// Search the user list for the following string /// Search the user list for the following string
/// </summary> /// </summary>

View File

@ -72,6 +72,7 @@
<Compile Include="OperationResponse.cs" /> <Compile Include="OperationResponse.cs" />
<Compile Include="Policy.cs" /> <Compile Include="Policy.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SortOptions.cs" />
<Compile Include="TimeLog.cs" /> <Compile Include="TimeLog.cs" />
<Compile Include="TimeLogList.cs" /> <Compile Include="TimeLogList.cs" />
<Compile Include="User.cs" /> <Compile Include="User.cs" />

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Interfaces
{
public enum SortOptions
{
None,
FirstNameAscending,
FirstNameDescending,
LastNameAscending,
LastNameDescending,
}
}

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
namespace Interfaces namespace Interfaces
{ {
@ -14,13 +15,15 @@ namespace Interfaces
public int TotalUserCount { get; set; } public int TotalUserCount { get; set; }
public List<User> Users { get; set; } public List<User> Users { get; set; }
public SortOptions SelectedSortOption { get; set; }
public int PageCount public int PageCount
{ {
get get
{ {
if (TotalUserCount < PageSize) if (TotalUserCount < PageSize)
return 1; return 1;
return (TotalUserCount / PageSize); return (int)Math.Ceiling(Convert.ToDouble(TotalUserCount) / Convert.ToDouble(PageSize));
} }
} }
public Group GroupFilter { get; set; } public Group GroupFilter { get; set; }

View File

@ -27,9 +27,7 @@ namespace SQLiteRepository
+ "select " + nameof(GroupDb.GroupId) + "select " + nameof(GroupDb.GroupId)
+ " from " + nameof(GroupDb) + " from " + nameof(GroupDb)
+ " where " + nameof(GroupDb.GroupName) + " = 'Archived') )" + " where " + nameof(GroupDb.GroupName) + " = 'Archived') )"
+ "order by " + "order by ? collate nocase ?, ? collate nocase ?";
+ nameof(UserIdentity.LastName) + " collate nocase, "
+ nameof(UserIdentity.FirstName) + " collate nocase";
public const string GET_ALL_USERS_PAGINATE = public const string GET_ALL_USERS_PAGINATE =
"select * from " + nameof(UserIdentity) + " ut " "select * from " + nameof(UserIdentity) + " ut "
@ -44,17 +42,17 @@ namespace SQLiteRepository
+ " from " + nameof(GroupDb) + " from " + nameof(GroupDb)
+ " where " + nameof(GroupDb.GroupName) + " = 'Archived') )" + " where " + nameof(GroupDb.GroupName) + " = 'Archived') )"
+ " order by " + " order by "
+ nameof(UserIdentity.LastName) + " collate nocase, " + "{0} collate nocase {1}, "
+ nameof(UserIdentity.FirstName) + " collate nocase " + "{2} collate nocase {3} "
+ "limit ? offset ?"; + "limit {4} offset {5}";
public const string GET_ALL_USERS_BY_GROUP = public const string GET_ALL_USERS_BY_GROUP =
"select u." + nameof(UserIdentity.Id) + ", u." + nameof(UserIdentity.FirstName) + ", u." + "select u." + nameof(UserIdentity.Id) + ", u." + nameof(UserIdentity.FirstName) + ", u." +
nameof(UserIdentity.LastName) + ", u." + nameof(UserIdentity.HoursPerWeek) + ", u." + nameof(UserIdentity.LastName) + ", u." + nameof(UserIdentity.HoursPerWeek) + ", u." +
nameof(UserIdentity.IsContractor) + " from " + nameof(UserIdentity) + " u left join " + nameof(UserIdentity.IsContractor) + " from " + nameof(UserIdentity) + " u left join " +
nameof(UserGroupJoinDb) + " ugj on ugj." + nameof(UserGroupJoinDb.UserId_FK) + " = u." + nameof(UserGroupJoinDb) + " ugj on ugj." + nameof(UserGroupJoinDb.UserId_FK) + " = u." +
nameof(UserIdentity.Id) + " where ugj." + nameof(UserGroupJoinDb.GroupId_FK) + "=? order by u." + nameof(UserIdentity.Id) + " where ugj." + nameof(UserGroupJoinDb.GroupId_FK) + "= {0} " +
nameof(UserIdentity.LastName) + " collate nocase, u." + nameof(UserIdentity.LastName) + " collate nocase"; "order by u.{1} collate nocase {2}, u.{3} collate nocase {4}";
public const string GET_USER_BY_ID = public const string GET_USER_BY_ID =
"select * from " + nameof(UserIdentity) + " where " + nameof(UserIdentity.Id) + "=?"; "select * from " + nameof(UserIdentity) + " where " + nameof(UserIdentity.Id) + "=?";
@ -96,7 +94,18 @@ namespace SQLiteRepository
nameof(TimeLogDb.SwipeEventDateTime) + " desc LIMIT 1"; nameof(TimeLogDb.SwipeEventDateTime) + " desc LIMIT 1";
public const string GET_TOTAL_USER_COUNT = public const string GET_TOTAL_USER_COUNT =
"select Max(" + nameof(UserIdentity.Id) + ") from " + nameof(UserIdentity); "select count(1) from " + nameof(UserIdentity) + " ut "
+ "where "
+ "EXISTS( select " + nameof(GroupDb.GroupId)
+ " from " + nameof(GroupDb)
+ " where " + nameof(GroupDb.GroupName) + " = 'Archived') "
+ "AND NOT EXISTS( select * from " + nameof(UserGroupJoinDb) + " ugp where "
+ nameof(UserGroupJoinDb.UserId_FK) + " = ut.Id"
+ " and " + nameof(UserGroupJoinDb.GroupId_FK) + " = ( "
+ "select " + nameof(GroupDb.GroupId)
+ " from " + nameof(GroupDb)
+ " where " + nameof(GroupDb.GroupName) + " = 'Archived') )";
//"select Max(" + nameof(UserIdentity.Id) + ") from " + nameof(UserIdentity);
public const string GET_USER_CONTRACTED_HOURS = public const string GET_USER_CONTRACTED_HOURS =
"select " + nameof(UserIdentity.HoursPerWeek) + " From UserIdentity where " + nameof(UserIdentity.Id) + "=?"; "select " + nameof(UserIdentity.HoursPerWeek) + " From UserIdentity where " + nameof(UserIdentity.Id) + "=?";

View File

@ -45,10 +45,10 @@ namespace SQLiteRepository
CheckForDbUpgrade(); CheckForDbUpgrade();
} }
public UserList GetUsers(int pageNumber = -1, int pageSize = -1, int groupId = -1) public UserList GetUsers(int pageNumber = -1, int pageSize = -1, int groupId = -1, SortOptions sort = SortOptions.None)
{ {
var ret = new UserList(); var ret = new UserList();
List<UserIdentity> users = GetUserList(groupId, pageSize, pageNumber); List<UserIdentity> users = GetUserList(groupId, pageSize, pageNumber, sort);
var userCount = GetUserCount(); var userCount = GetUserCount();
@ -555,24 +555,56 @@ namespace SQLiteRepository
return _connection.ExecuteScalar<int>(SQLiteProcedures.GET_TOTAL_USER_COUNT); return _connection.ExecuteScalar<int>(SQLiteProcedures.GET_TOTAL_USER_COUNT);
} }
private List<UserIdentity> GetUserList(int groupId = -1, int pageSize=-1, int pageNumber=-1) private List<UserIdentity> GetUserList(int groupId = -1, int pageSize = -1, int pageNumber = -1, SortOptions sort = SortOptions.None)
{ {
List<UserIdentity> users; List<UserIdentity> users;
var orderByFirst = nameof(UserIdentity.LastName);
var firstOrderDir = "ASC";
var orderBySecond = nameof(UserIdentity.FirstName);
var secondOrderDir = "ASC";
if (sort != SortOptions.None)
{
switch (sort)
{
case SortOptions.FirstNameAscending:
orderByFirst = nameof(UserIdentity.FirstName);
firstOrderDir = "ASC";
orderBySecond = nameof(UserIdentity.LastName);
secondOrderDir = "ASC";
break;
case SortOptions.FirstNameDescending:
orderByFirst = nameof(UserIdentity.FirstName);
firstOrderDir = "DESC";
orderBySecond = nameof(UserIdentity.LastName);
secondOrderDir = "ASC";
break;
case SortOptions.LastNameDescending:
firstOrderDir = "DESC";
break;
}
}
if (pageNumber != -1 && pageSize != -1) if (pageNumber != -1 && pageSize != -1)
{ {
users = _connection.Query<UserIdentity>(SQLiteProcedures.GET_ALL_USERS_PAGINATE, var qString = string.Format(SQLiteProcedures.GET_ALL_USERS_PAGINATE, orderByFirst, firstOrderDir,
pageSize, (pageNumber - 1) * pageSize); orderBySecond, secondOrderDir, pageSize, (pageNumber - 1) * pageSize);
users = _connection.Query<UserIdentity>(qString);
} }
else if (groupId != -1) else if (groupId != -1)
{ {
var qString = string.Format(SQLiteProcedures.GET_ALL_USERS_BY_GROUP, groupId,
orderByFirst, firstOrderDir,
orderBySecond, secondOrderDir);
users = users =
_connection.Query<UserIdentity>( _connection.Query<UserIdentity>(qString);
SQLiteProcedures.GET_ALL_USERS_BY_GROUP,
groupId);
} }
else else
{ {
users = _connection.Query<UserIdentity>(SQLiteProcedures.GET_ALL_USERS); users = _connection.Query<UserIdentity>(SQLiteProcedures.GET_ALL_USERS,
orderByFirst, firstOrderDir,
orderBySecond, secondOrderDir);
} }
return users; return users;

View File

@ -30,17 +30,18 @@ namespace WindowsDataCenter
public IHttpActionResult GetUsers([FromUri] string query = "" public IHttpActionResult GetUsers([FromUri] string query = ""
, [FromUri] int pageSize = -1 , [FromUri] int pageSize = -1
, [FromUri] int pageNumber = -1 , [FromUri] int pageNumber = -1
, [FromUri] int groupId = -1) , [FromUri] int groupId = -1
, [FromUri] SortOptions sort = SortOptions.None)
{ {
_logger.Trace("GetUsers called with arguments >> query: {0}, pageSize: {1}, pageNumber: {2}, groupFilter: {3}", query, pageSize, pageNumber, groupId); _logger.Trace("GetUsers called with arguments >> query: {0}, pageSize: {1}, pageNumber: {2}, groupFilter: {3}, sort: {4}", query, pageSize, pageNumber, groupId, sort);
pageNumber = pageNumber == -1 ? 1 : pageNumber; pageNumber = pageNumber == -1 ? 1 : pageNumber;
pageSize = GetPageSize(pageSize); pageSize = GetPageSize(pageSize);
var userList = query != string.Empty var userList = query != string.Empty
? _repo.Search(query) ? _repo.Search(query)
: groupId != -1 : groupId != -1
? _repo.GetUsers(groupId: groupId) ? _repo.GetUsers(groupId: groupId, sort:sort)
: _repo.GetUsers(pageNumber, pageSize); : _repo.GetUsers(pageNumber, pageSize, sort:sort);
_logger.Trace("Got UserList from Repository, UserCount: {0}", userList.UserCount); _logger.Trace("Got UserList from Repository, UserCount: {0}", userList.UserCount);
if (query != string.Empty) if (query != string.Empty)
{ {
@ -57,6 +58,7 @@ namespace WindowsDataCenter
userList.PageNumber = pageNumber; userList.PageNumber = pageNumber;
userList.PageSize = pageSize; userList.PageSize = pageSize;
userList.SelectedSortOption = sort;
_logger.Trace("Returning UserList from GetUsers."); _logger.Trace("Returning UserList from GetUsers.");
var msg = Request.CreateResponse(HttpStatusCode.OK, userList); var msg = Request.CreateResponse(HttpStatusCode.OK, userList);

View File

@ -86,8 +86,23 @@
<thead> <thead>
<tr> <tr>
<th class="col-md-1"></th> <th class="col-md-1"></th>
<th class="col-md-3">First Name</th> <th class="col-md-3">
<th class="col-md-3">Last Name</th> <div class="col-md-1">
<a href="#users?sort=firstAsc"><span data-bind="css:{ 'text-success': $root.sortIsActive('firstAsc')}" class="glyphicon glyphicon-chevron-up"></span></a>
</div>
<div class="col-md-1">
<a href="#users?sort=firstDesc"><span data-bind="css:{ 'text-success': $root.sortIsActive('firstDesc')}" class="glyphicon glyphicon-chevron-down"></span></a>
</div>
<span class="col-md-9">First Name</span>
</th>
<th class="col-md-3">
<div class="col-md-1">
<a href="#users?sort=lastAsc"><span data-bind="css:{ 'text-success': $root.sortIsActive('lastAsc')}" class="glyphicon glyphicon-chevron-up"></span></a>
</div>
<div class="col-md-1">
<a href="#users?sort=lastDesc"><span data-bind="css:{ 'text-success': $root.sortIsActive('lastDesc')}"class="glyphicon glyphicon-chevron-down"></span></a>
</div>
<span class="col-md-9">Last Name</span></th>
<th class="col-md-1 text-center">Contractor</th> <th class="col-md-1 text-center">Contractor</th>
<th/> <th/>
<th/> <th/>

View File

@ -1,4 +1,10 @@
function DataVM() { const SortOptions = {
LastAsc: "LastNameAscending",
LastDesc: "LastNameDescending",
FirstAsc: "FirstNameAscending",
FirstDesc: "FirstNameDescending"
};
function DataVM() {
"use strict"; "use strict";
var self = this; var self = this;
self.helpers = new Helpers(); self.helpers = new Helpers();
@ -23,6 +29,7 @@
self.policyChangeDate = ko.observable(null); self.policyChangeDate = ko.observable(null);
self.policyChangeAuthor = ko.observable(null); self.policyChangeAuthor = ko.observable(null);
self.policyVersion = ko.observable(null); self.policyVersion = ko.observable(null);
self.selectedSort = ko.observable(null);
self.apiEndpoints = { self.apiEndpoints = {
root: "http://localhost:8800", root: "http://localhost:8800",
getUserList: "/api/users", getUserList: "/api/users",
@ -156,7 +163,7 @@
var args = [ var args = [
{ {
key: "groupId", key: "groupId",
value: datacreateContextMenu value: data
} }
]; ];
var url = self.helpers.createRequestUrl("users", args, false, false); var url = self.helpers.createRequestUrl("users", args, false, false);
@ -221,25 +228,32 @@
}); });
} }
}; };
self.getUserList = function (pageSize, pageNumber, groupId) { self.getUserList = function (pageSize, pageNumber, groupId, sort) {
var args = null; var args = [];
if (pageSize && pageNumber) { if (pageSize && pageNumber) {
args = [ args.push(
{ {
key: "pageSize", key: "pageSize",
value: pageSize value: pageSize
}, });
args.push(
{ {
key: "pageNumber", key: "pageNumber",
value: pageNumber value: pageNumber
});
} }
]; if (groupId) {
} else if(groupId) { args.push(
args = [
{ {
key: "groupId", key: "groupId",
value: groupId value: groupId
}]; });
}
if (sort) {
args.push({
key: "sort",
value: sort
});
} }
var url = self.helpers.createRequestUrl(self.apiEndpoints.getUserList, args, false); var url = self.helpers.createRequestUrl(self.apiEndpoints.getUserList, args, false);
$.getJSON(url, function (res) { $.getJSON(url, function (res) {
@ -261,9 +275,9 @@
self.assignErrorObject(errObj.errorCode, errObj.errorMessage, "getUserList"); self.assignErrorObject(errObj.errorCode, errObj.errorMessage, "getUserList");
}); });
}; };
self.searchUsers = function(query) { self.searchUsers = function(query, sort) {
var url = self.helpers.createRequestUrl(self.apiEndpoints.getUserList, var url = self.helpers.createRequestUrl(self.apiEndpoints.getUserList,
[{ key: "query", value: query }], false, false); [{ key: "query", value: query }, {key:"sort",value:sort}], false, false);
$.getJSON(url, $.getJSON(url,
function(res) { function(res) {
self.userList(res); self.userList(res);
@ -426,12 +440,46 @@
maxDate: moment(date).endOf("week") maxDate: moment(date).endOf("week")
}); });
}; };
function convertSortOption(opt) {
if (!opt) {
return SortOptions.LastAsc;
}
if (opt === "firstAsc") {
return SortOptions.FirstAsc;
}
if (opt === "firstDesc") {
return SortOptions.FirstDesc;
}
if (opt === "lastAsc") {
return SortOptions.LastAsc;
}
if (opt === "lastDesc") {
return SortOptions.LastDesc;
}
};
self.sortIsActive = function (option) {
if (option === "firstAsc" && self.userList().SelectedSortOption === SortOptions.FirstAsc) {
return true;
}
if (option === "firstDesc" && self.selectedSort() === SortOptions.FirstDesc) {
return true;
}
if (option === "lastAsc" && self.selectedSort() === SortOptions.LastAsc) {
return true;
}
if (option === "lastDesc" && self.selectedSort() === SortOptions.LastDesc) {
return true;
}
return false;
};
Sammy(function () { Sammy(function () {
this.get("#users", function () { this.get("#users", function () {
var query = this.params.query; var query = this.params.query;
var pageSize = this.params.pageSize; var pageSize = this.params.pageSize;
var pageNumber = this.params.pageNumber; var pageNumber = this.params.pageNumber;
var groupId = this.params.groupId; var groupId = this.params.groupId;
var sort = convertSortOption(this.params.sort);
console.log(sort);
self.chosenMenuItemId("Home"); self.chosenMenuItemId("Home");
self.groupsList(null); self.groupsList(null);
self.chosenUserDetails(null); self.chosenUserDetails(null);
@ -444,11 +492,11 @@
} }
self.getGroups(function (data) { self.groupsList(data); }); self.getGroups(function (data) { self.groupsList(data); });
if (query) if (query)
self.searchUsers(query); self.searchUsers(query, sort);
else if (groupId && groupId > 0) else if (groupId && groupId > 0)
self.getUserList(null, null, groupId); self.getUserList(null, null, groupId, sort);
else else
self.getUserList(pageSize, pageNumber); self.getUserList(pageSize, pageNumber, null,sort);
}); });
this.get("#userData/:userId", function () { this.get("#userData/:userId", function () {
self.chosenMenuItemId("Data"); self.chosenMenuItemId("Data");
@ -528,7 +576,6 @@
this.post("#manualLog", this.post("#manualLog",
function() { function() {
self.createManualLog(self.manualLog()); self.createManualLog(self.manualLog());
$('#manualLogDialog').modal("hide"); $('#manualLogDialog').modal("hide");
//self.goToTimeLogs(self.chosenTimeLogUserId, null, [{ key: "selectedDate", value: self.selectedTimeLogDate() }]); //self.goToTimeLogs(self.chosenTimeLogUserId, null, [{ key: "selectedDate", value: self.selectedTimeLogDate() }]);
}); });