From 0bb6c8bc0836f4a56b56c805c7d4eae979744231 Mon Sep 17 00:00:00 2001 From: "chris.watts90@outlook.com" Date: Mon, 6 Feb 2017 22:18:09 +0000 Subject: [PATCH] corrected uiPages method to return correct home endpoint. changed references to User.Id to User.UserId. added relativePath option to createRequestUrl(..). added documentation to returnButtonClick and createRequestUrl methods. changed initDatePicker to have separate assign handler method to make code simpler. changed all URL references to relative urls, no longer JSONP/crossdomain compatible. tidied code. added query parameter to home/users page. added search getJson method. changed all property names to match the WebApi endpoint. added js to conditionally highlight the datepicker row on hover. changed script and css references to proper CDN urls. tidied html. added form for querying user list. UserController will conditionally choose to GetUsersList or Search, based on Query Parameter. Add search method to IRepository Implement Search method on IRepository interface added search procedure to SQLiteProcedures added Query property to UserList to allow UI to display the last filter/query parameter. --- .../Interfaces/IRepository.cs | 9 + .../WindowsDataCenter/Interfaces/UserList.cs | 1 + .../SQLiteRepository/SQLiteProcedures.cs | 1 + .../SQLiteRepository/SQLiteRepository.cs | 39 +++ .../Controllers/UsersController.cs | 5 +- .../WindowsDataCenter/www/index.html | 79 +++--- .../WindowsDataCenter/www/spa.js | 268 ++++++++++-------- 7 files changed, 245 insertions(+), 157 deletions(-) diff --git a/DataCenter_Windows/WindowsDataCenter/Interfaces/IRepository.cs b/DataCenter_Windows/WindowsDataCenter/Interfaces/IRepository.cs index 8cf0ea0..2a8c720 100644 --- a/DataCenter_Windows/WindowsDataCenter/Interfaces/IRepository.cs +++ b/DataCenter_Windows/WindowsDataCenter/Interfaces/IRepository.cs @@ -11,6 +11,15 @@ /// UserList GetUsers(); /// + /// Search the user list for the following string + /// + /// string to search the user store for. + /// + /// Returns with full list of users, + /// plus a total user count. Pagination options are supported. + /// + UserList Search(string searchParam); + /// /// Get details about a single user in the system base on their Id /// /// diff --git a/DataCenter_Windows/WindowsDataCenter/Interfaces/UserList.cs b/DataCenter_Windows/WindowsDataCenter/Interfaces/UserList.cs index 303d7d3..701dfe7 100644 --- a/DataCenter_Windows/WindowsDataCenter/Interfaces/UserList.cs +++ b/DataCenter_Windows/WindowsDataCenter/Interfaces/UserList.cs @@ -8,6 +8,7 @@ namespace Interfaces { Users = new List(); } + public string Query { get; set; } public int UserCount { get { return Users.Count; } } public List Users { get; set; } public int PageSize { get; set; } diff --git a/DataCenter_Windows/WindowsDataCenter/SQLiteRepository/SQLiteProcedures.cs b/DataCenter_Windows/WindowsDataCenter/SQLiteRepository/SQLiteProcedures.cs index 959b755..1ed3431 100644 --- a/DataCenter_Windows/WindowsDataCenter/SQLiteRepository/SQLiteProcedures.cs +++ b/DataCenter_Windows/WindowsDataCenter/SQLiteRepository/SQLiteProcedures.cs @@ -14,5 +14,6 @@ namespace SQLiteRepository public const string UPDATE_CARD_USER_ID = "update CardUniqueId set UserId_FK=? where Id=?"; + public const string SEARCH_USER_LIST = "SELECT * FROM[UserIdentity] where(FirstName Like ? OR LastName Like ?)"; } } \ No newline at end of file diff --git a/DataCenter_Windows/WindowsDataCenter/SQLiteRepository/SQLiteRepository.cs b/DataCenter_Windows/WindowsDataCenter/SQLiteRepository/SQLiteRepository.cs index 4afd5f6..8dd9c76 100644 --- a/DataCenter_Windows/WindowsDataCenter/SQLiteRepository/SQLiteRepository.cs +++ b/DataCenter_Windows/WindowsDataCenter/SQLiteRepository/SQLiteRepository.cs @@ -58,6 +58,45 @@ namespace SQLiteRepository return ret; } + public UserList Search(string searchParam) + { + var ret = new UserList(); + searchParam = string.Format("%{0}%", searchParam); + var users = _connection.Query( + SQLiteProcedures.SEARCH_USER_LIST, + searchParam, searchParam); + + if (!users.Any()) + { + ret.PageNumber = 1; + ret.PageSize = 20; + return ret; + } + + foreach (var user in users) + { + var userObj = ChangeToUserObject(user); + var cards = _connection.Query( + SQLiteProcedures.GET_CARDS_BY_USER_ID, + user.Id); + + foreach (var card in cards) + { + userObj.AssociatedIdentifiers.Add(new Identifier() + { + UniqueId = card.CardUId, + IsAssociatedToUser = true, + Id = card.Id + }); + } + ret.Users.Add(userObj); + } + ret.PageSize = 20; + ret.PageNumber = 1; + + return ret; + } + public User GetUser(int id) { var ret = new User(); diff --git a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/Controllers/UsersController.cs b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/Controllers/UsersController.cs index 0322f2d..2ed6dd7 100644 --- a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/Controllers/UsersController.cs +++ b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/Controllers/UsersController.cs @@ -19,9 +19,10 @@ namespace WindowsDataCenter [HttpGet] [Route("")] - public IHttpActionResult GetUsers([FromUri] int pageSize = -1, [FromUri] int pageNumber =-1) + public IHttpActionResult GetUsers([FromUri] string query = "",[FromUri] int pageSize = -1, [FromUri] int pageNumber =-1) { - var userList = _repo.GetUsers(); + var userList = query == string.Empty ? _repo.GetUsers() : _repo.Search(query); + userList.Query = query == string.Empty ? null : query; var msg = Request.CreateResponse(HttpStatusCode.OK, userList); diff --git a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/index.html b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/index.html index ba936f5..4c38957 100644 --- a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/index.html +++ b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/index.html @@ -2,9 +2,19 @@ Flexi Time Data Viewer - - - + + + + + + + + + + + + -
+
-
+
- + - -
-
+ + +

-
- +
+
@@ -76,37 +86,45 @@ -
+

- +
- - + +
- +
- + +
+
+
+ + + + +

- +
- + - +
@@ -122,21 +140,21 @@
- + - +

- +
-
+
@@ -150,12 +168,9 @@
-
-

Content

ID
@@ -230,13 +245,11 @@ - - - - - - + + \ No newline at end of file diff --git a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/spa.js b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/spa.js index 37fff59..b969143 100644 --- a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/spa.js +++ b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/spa.js @@ -1,18 +1,4 @@ -//(function ($) { -// "use strict"; -// $.fn.clickToggle = function (func1, func2) { -// var funcs = [func1, func2]; -// this.data("toggleclicked", 0); -// this.click(function () { -// var data = $(this).data(); -// var tc = data.toggleclicked; -// $.proxy(funcs[tc], this)(); -// data.toggleclicked = (tc + 1) % 2; -// }); -// return this; -// }; -//}(jQuery)); -function DataVM() { +function DataVM() { "use strict"; var self = this; self.menuOptions = ["Users", "Data", "Stats", "Other"]; @@ -34,14 +20,29 @@ function DataVM() { users: "users", userDetails: "userData", timeLogs: "timelogs", - home: this.users + home: function () { return this.users; } }; self.goToMenuOption = function (menu) { location.hash = menu; console.log("goToMenuOption: " + menu); }; - self.goToUserDetails = function (user) { location.hash = self.uiPages.userDetails + "/" + user.Id; }; - self.goToTimeLogs = function (user) { location.hash= "timelogs" + "/" + user.Id; }; - self.createRequestUrl = function (routePath, params, requiresCallback) { + self.goToUserDetails = function (user) { location.hash = self.uiPages.userDetails + "/" + user.UserId; }; + self.goToTimeLogs = function (user) { location.hash = "timelogs" + "/" + user.UserId; }; + /** + * Create a request URL - references apiEndpoints object to construct url with args, and optional callback url. + * @param {string} routePath + * @param {Array} params - Key, Value object detailing the param name (key) and value (value). + * @param {boolean} requiresCallback - True - add callback function for JSONP/CORS. + * @param {boolean} isRelativePath - True, create a relative URL (without root). + * @returns {string} the url generated + * @example + * createRequestUrl("/api/endpoint", [{key:"param", value:"value"}], true, false); + * returns: "http://192.168.2.2/api/endpoint?param=value&callback=?" + */ + self.createRequestUrl = function (routePath, params, requiresCallback, isRelativePath) { var appender = "?"; - var url = self.apiEndpoints.root + routePath; + var url = ""; + if (isRelativePath) { + url = self.apiEndpoints.root; + } + url = url + routePath; if (params !== undefined && params !== null) { if (params.length > 0) { @@ -56,8 +57,13 @@ function DataVM() { } return url; }; - //this function looks for a custom attribute called "pagedestination". - //if this is used as a clickhandler, the pagedestination attribute should name the page you want. + /** + * Function to redirect to a page in the sammy.js eco system. + * Relies on "pagedestination" tag in the html. This is a button click handler. + * @param {Object} data - dunno? + * @param {Object} event - handle to the button that was clicked. + * @returns {nothing} - redirects to the url referenced by the pageDestination tag. + */ self.returnButtonClick = function (data, event) { var target = null; if (event.target) target = event.target; @@ -70,102 +76,145 @@ function DataVM() { break; } } - console.log(destination); - self.goToMenuOption(destination); //redirect to whereever the button is telling us to go.. + if (destination !== "") { + console.log(destination); + self.goToMenuOption(destination); //redirect to whereever the button is telling us to go.. + } } else { console.log("target is null, going nowhere"); } }; + self.initDatePicker = function () { + $("#weeklyDatePicker").datepicker({ + weekStart: 1, + maxViewMode: 2, + endDate: "+0d", + todayBtn: true, + format: "yyyy-mm-dd", + todayHighlight: true, + calendarWeeks: true + }); + $("#weeklyDatePicker").datepicker("setDate", new Date().toDateString("yyyy-mm-dd")); + console.log("finished init"); + }; + self.assignHandler = function () { + //console.log('shall I assign?'); + var elem = $("#weeklyDatePicker")[0]; + var data = jQuery.hasData(elem) && jQuery._data(elem); + if (!data.events.changeDate) { + //console.log("assigning.."); + $("#weeklyDatePicker").on("changeDate", function (e) { + var kk = e.date; + //console.log(kk); + //console.log( + // "1: Iso Week Number: " + moment(kk).isoWeek() + " of " + + // moment(kk).weeksInYear() + //); + //console.log("before: " + self.selectedCalendarWeek()); + self.selectedCalendarWeek(moment(kk).isoWeek()); + //console.log("after: " + self.selectedCalendarWeek()); + }); + } + } self.getUserList = function () { - console.log("beginGetUserData"); - var url = self.createRequestUrl(self.apiEndpoints.getUserList, null, true); + //console.log("beginGetUserData"); + var url = self.createRequestUrl(self.apiEndpoints.getUserList, null, false); console.log(url); $.getJSON(url, function (res) { - console.log("getting user data"); - console.log(res); + //console.log("getting user data"); + //console.log(res); self.userList(res); }).fail(function () { console.log("error - getusers"); }); }; + self.searchUsers = function(query) { + var url = self.createRequestUrl(self.apiEndpoints.getUserList, + [{ key: "query", value: query }], false, false); + $.getJSON(url, + function(res) { + self.userList(res); + }).fail(function(res) { + self.goToMenuOption(self.uiPages.home()); + } + ); + }; self.getUserDetails = function (userId) { - console.log("beginGetUserDetailsData"); - //var url = self.apiEndpoints.root + self.apiEndpoints.getUserDetails + "/" + userId; - var url = self.createRequestUrl(self.apiEndpoints.getUserDetails + "/" + userId, null, true); - console.log(url); + //console.log("beginGetUserDetailsData"); + var url = self.createRequestUrl(self.apiEndpoints.getUserDetails + "/" + userId, null, false); $.getJSON(url, function (res) { - console.log("got user data"); - //console.log(res); + //console.log("got user data"); self.chosenUserDetails(res); - //console.log(self.chosenUserDetails()); }).fail(function () { console.log("error - getuserdetails"); + self.goToMenuOption(self.uiPages.home()); }); }; - self.handleEditedUser = function (user) { - console.log("Post Edited User: " + user.Id); - //var url = self.apiEndpoints.root + self.apiEndpoints.editUser; - var url = self.createRequestUrl(self.apiEndpoints.editUser, null, false); + self.submitChangedUser = function (user) { + //console.log("Post Edited User: " + user.UserId); var url = self.apiEndpoints.editUser; console.log("posting to.." + url); $.post(url, user, function () { - console.log("finished posting.."); }, "json") - //.done(function () { - // //self.goToMenuOption("users"); - // //self.chosenUserDetails(null); - // //self.goToMenuOption(self.menuOptions.home); - // }) - .fail(function (response) { - //due to unique way a 201 is a fail... - if (response.status === 201) { - self.goToMenuOption("users"); - self.chosenUserDetails(null); - } else { - console.log("error - post edited user"); - console.log(self.chosenUserDetails().UserId); - self.goToMenuOption(self.menuOptions.home); - } - }); + .done(function () { + self.goToMenuOption("users"); + self.chosenUserDetails(null); + }) + .fail(function (response) { + if (response.status === 201) { + self.goToMenuOption("users"); + self.chosenUserDetails(null); + } else { + console.log("error - post edited user"); + self.goToMenuOption(self.uiPages.home()); + } + }); }; self.getTimeLogData = function (userId, calendarWeek) { - console.log("begin get TimeLog Data"); + //console.log("begin get TimeLog Data"); var url = self.createRequestUrl(self.apiEndpoints.getTimeLogs, [{ key: "userId", value: userId }, { key: "calendarWeek", value: calendarWeek }], - true); + false); $.getJSON(url, function (res) { - console.log("got user timelog data"); - //console.log(res); + //console.log("got user timelog data"); self.userTimeLogData(res); - }).fail(function () { - console.log("error - getuserdetails"); - self.goToMenuOption(self.uiPages.home); //go home. + self.initDatePicker(); + self.assignHandler(); + }).fail(function (resp) { + if (resp.status === 200) { + self.userTimeLogData(resp.content); + //console.log(resp.content); + } else { + console.log("error - getuserdetails"); + self.goToMenuOption(self.uiPages.home()); //go home. + } }); }; self.getUnassignedCardData = function () { - console.log("begin get unassigned cards"); - var url = self.createRequestUrl(self.apiEndpoints.getUnassignedCards, null, true); + //console.log("begin get unassigned cards"); + var url = self.createRequestUrl(self.apiEndpoints.getUnassignedCards, null, false); $.getJSON(url, function (res) { - console.log("got unassigned card data"); //console.log(res); self.unassignedCardData(res); }).fail(function () { console.log("error - getuserdetails"); - //self.goToMenuOption(self.uiPages.home); //go home. // no, dont want to go home here, cos just means we dont have any details? }); }; Sammy(function () { this.get("#users", function () { - console.log(this.params.menuOption); + var query = this.params.query; + console.log(this.params.query); self.chosenMenuItemId("Users"); self.chosenUserDetails(null); self.userList(null); self.userTimeLogData(null); - self.getUserList(); + if (query) + self.searchUsers(query); + else + self.getUserList(); //$.get("http://localhost:3000", { menu: this.params.menu }, self.chosenMenuData); }); this.get("#userData/:userId", function () { - console.log("userData userId: " + this.params.userId); console.log("getting details for user: " + this.params.userId); self.chosenMenuItemId("Data"); //todo: change this! (replace with actual get timelogs call) self.userList(null); @@ -176,82 +225,57 @@ function DataVM() { }); this.get("#timelogs/:userId", function () { console.log("get user time logs, userID: " + this.params.userId); - self.chosenMenuItemId('Other'); + self.chosenMenuItemId("Other"); self.userList(null); self.chosenUserDetails(null); - //self.selectedCalendarWeek = 0; self.getTimeLogData(this.params.userId, self.selectedCalendarWeek()); - self.userTimeLogData({ "test": 0 }); - self.initDatePicker(); - //$( "#datepicker" ).datepicker(); }); this.get("#newUser", function () { console.log("creating new user"); - self.chosenMenuItemId('newUser'); + self.chosenMenuItemId("newUser"); self.userList(null); self.userTimeLogData(null); self.chosenUserDetails({ - "Id": -1, "UserId": -1, "FirstName": null, "LastName": null, "HoursPerWeek": null, - "AssociatedCards": null + "AssociatedIdentifiers": [], + "IsContractor": false }); self.getUnassignedCardData(); }); - this.get("#stats", function() { + this.get("#stats", function () { self.goToMenuOption("users"); }); - this.post("#edituser", function (context) { - $.each(self.chosenUserDetails().AssociatedCards, function (k, v) { - if (v.IsSelected !== true) { - console.log("removing.."); - self.chosenUserDetails().AssociatedCards.splice(k, 1); - console.log(v.CardUId); - } - }); + this.post("#edituser", function () { + $.each(self.chosenUserDetails().AssociatedIdentifiers, + function (k, v) { + if (v.IsAssociatedToUser !== true) { + console.log("removing.."); + self.chosenUserDetails().AssociatedIdentifiers.splice(k, 1); + console.log(v.UniqueId); + } + }); $.each(self.unassignedCardData().data, function (k, v) { - if (v.IsSelected == true) { - self.chosenUserDetails().AssociatedCards.push(v); - console.log(v.CardUId); + if (v.IsAssociatedToUser === true) { + self.chosenUserDetails().AssociatedIdentifiers.push(v); + console.log(v.UniqueId); } }); - console.log(self.chosenUserDetails()); - self.handleEditedUser(self.chosenUserDetails()); - //self.goToMenuOption(self.menuOptions.home); + //console.log(self.chosenUserDetails()); + self.submitChangedUser(self.chosenUserDetails()); return false; }); //default route (home page) this.get("", function () { this.app.runRoute("get", "#" + self.uiPages.users) }); }).run(); - self.initDatePicker = function () { - "use strict"; - //Initialize the datePicker(I have taken format as mm-dd-yyyy, you can - //have your owh) - $("#weeklyDatePicker").datepicker({ - weekStart: 1, - maxViewMode: 2, - endDate: "+0d", - todayBtn: true, - format: "yyyy-mm-dd", - todayHighlight: true, - calendarWeeks: true - }); - $("#weeklyDatePicker").on("changeDate", function (e) { - "use strict"; - console.log("in here2"); - var kk = e.date; - console.log(kk); - console.log( - "1: Iso Week Number: " + moment(kk).isoWeek() + " of " + - moment(kk).weeksInYear() - ); - console.log("before: " + self.selectedCalendarWeek()); - self.selectedCalendarWeek(moment(kk).isoWeek()); - console.log("after: " + self.selectedCalendarWeek()); - }); - //console.log("finished init"); - }; }; -ko.applyBindings(new DataVM()); \ No newline at end of file +ko.applyBindings(new DataVM()); + +$(document).on("mouseenter", ".datepicker-days tbody tr", function () { + $(this).addClass('highlight'); +}); +$(document).on("mouseleave", ".datepicker-days tbody tr", function () { + $(this).removeClass('highlight'); +}); \ No newline at end of file