diff --git a/SaxoniteCoreWeb/Base/Middleware/SaxModelBinder.cs b/SaxoniteCoreWeb/Base/Middleware/SaxModelBinder.cs index 206d471..31b74b3 100644 --- a/SaxoniteCoreWeb/Base/Middleware/SaxModelBinder.cs +++ b/SaxoniteCoreWeb/Base/Middleware/SaxModelBinder.cs @@ -61,10 +61,10 @@ namespace Saxonite.Net.Core.Web.Base.Middleware { //if (SaxSystem.DebugMode) Logger.Debug(fn + " Form property " + modelName + ": '" + result.Model.ToString() + "."); object originalValue = result.Model; - // Anything that looks like a password will be left as is. - bool password = modelName.ContainsCI("Password"); - // Exclude properties that retain leading/trailing whitespace. - if (result.Model is string) + // Anything that looks like a password will be left as is. + bool password = modelName.ContainsCI("Password"); + // Exclude properties that retain leading/trailing whitespace. + if (result.Model is string) { if (!password) { @@ -97,13 +97,15 @@ namespace Saxonite.Net.Core.Web.Base.Middleware } } // Log property details. - if (result.Model != null && SaxSystem.DebugMode && - // Ignore passwords. - !password && - // Ignore items that correspond with objects in the model rather than values. - !result.Model.ToString().Equals(result.Model.GetType().FullName) && - // Ignore form files. - !result.Model.ToString().ContainsCI("Microsoft.AspNetCore.Http.IFormFile")) + if (SaxSystem.DebugMode && + // Ignore blank values. + result.Model != null && !StringUtil.IsBlank(result.Model.ToString()) && + // Ignore passwords. + !password && + // Ignore items that correspond with objects in the model rather than values. + !result.Model.ToString().Equals(result.Model.GetType().FullName) && + // Ignore form files. + !result.Model.ToString().ContainsCI("Microsoft.AspNetCore.Http.IFormFile")) SaLogger.Debug(SysUtil.Fn + " Form property " + modelName + ": '" + result.Model.ToString() + "'." + (originalValue != null && !originalValue.Equals(result.Model) ? " Original value: '" + originalValue.ToString() + "'" : "")); base.SetProperty(bindingContext, modelName, propertyMetadata, result); } diff --git a/SaxoniteCoreWeb/Base/Procs/SaxGolfUsgaDataLookup.cs b/SaxoniteCoreWeb/Base/Procs/SaxGolfUsgaDataLookup.cs index 1a47ed6..572f623 100644 --- a/SaxoniteCoreWeb/Base/Procs/SaxGolfUsgaDataLookup.cs +++ b/SaxoniteCoreWeb/Base/Procs/SaxGolfUsgaDataLookup.cs @@ -31,10 +31,17 @@ namespace Saxonite.Net.Core.Web.Base.Procs { //------------------------------------------------------------------- // Definitions. + public enum Task + { + CleanseCourseCaptions, + } + public new class Config : SaxProcess_Web.Config { public Config(SaxSessionContext sessionContext, DbUpdateInfo dbUpdateInfo) : base(sessionContext, dbUpdateInfo) { } + /// Tasks to be run under this instance of the USGA Data Lookup process. + public HashSet Tasks { get; } = new HashSet(); } public class CountryProvinceCodes @@ -154,6 +161,18 @@ namespace Saxonite.Net.Core.Web.Base.Procs // Run the process. protected override bool Run() { + // Execute selected tasks. + foreach (Task task in config.Tasks) + { + switch (task) + { + case Task.CleanseCourseCaptions: + CleanseCourseCaptions(); + break; + } + if (!OK) break; + } + return OK; } @@ -636,26 +655,37 @@ namespace Saxonite.Net.Core.Web.Base.Procs /// /// private void SetCourseCaption(CourseData course) + { + course.Caption = GetCourseCaption(course.FacilityName, course.CourseName); + } + + /// + /// Combine facility and course names as required to generate course caption. + /// - NB Course name is sufficient if same as facility name. + /// + /// + private String GetCourseCaption(String facilityName, String courseName) { // Facility (club) name. // - Strip id in brackets + anything following and remove generic phrases (golf club, hotel etc). - String facilityName = CleanseFacilityName(Regex.Replace(course.FacilityName, "\\s*\\(\\s*\\d+\\s*\\).*$", "")); + facilityName = CleanseFacilityName(Regex.Replace(facilityName, "\\s*\\(\\s*\\d+\\s*\\).*$", "")); // Course name. // - Remove id in brackets (occasionally found). // - Remove generic phrases (including for facility name). - String courseName = CleanseCourseName(Regex.Replace(course.CourseName, "\\(\\s*\\d+\\s*\\)", "")); - course.Caption = courseName; + courseName = CleanseCourseName(Regex.Replace(courseName, "\\(\\s*\\d+\\s*\\)", "")); + String courseCaption = courseName; if (!CourseNameMatchesFacility(facilityName, courseName)) - course.Caption = facilityName + " - " + courseName; + courseCaption = facilityName + " - " + courseName; + return courseCaption; } /// /// Cleanse Facility name by removing generic phrases, eg. golf club, hotel etc. /// - public static String CleanseFacilityName(String facilityName) + private String CleanseFacilityName(String facilityName) { - facilityName = Regex.Replace(facilityName, "Hotel(,\\s*Golf)?\\s*(and|&)\\s*(Golf|Country|Sailing|Spa)(\\s*Club)?", "", RegexOptions.IgnoreCase); - facilityName = Regex.Replace(facilityName, "Golf(\\s*Club)?\\s*(and|&)\\s*(Hotel|Country|Sailing|Spa)\\s*Club", "", RegexOptions.IgnoreCase); + facilityName = Regex.Replace(facilityName, "Hotel(,\\s*Golf)?\\s*(and|&)\\s*(Golf|Country|Health|Sailing|Spa)(\\s*Club)?", "", RegexOptions.IgnoreCase); + facilityName = Regex.Replace(facilityName, "Golf(\\s*Club)?\\s*(and|&)\\s*(Hotel|Country|Fitness|Sailing|Spa)(\\s*Club)", "", RegexOptions.IgnoreCase); facilityName = Regex.Replace(facilityName, "Golf\\s*(Club|Course|Links|Centre|Academy|Resort|Klub)(\\s*(Ltd|Limited))?", "", RegexOptions.IgnoreCase); facilityName = Regex.Replace(facilityName, "Club\\s*de\\s*Golf", "", RegexOptions.IgnoreCase); facilityName = Regex.Replace(facilityName, "\\s*(Golf|Country\\s*Club|Club|Ltd)\\s*$", "", RegexOptions.IgnoreCase); @@ -684,6 +714,93 @@ namespace Saxonite.Net.Core.Web.Base.Procs return false; } + //------------------------------------------------------------------ + public bool CleanseCourseCaptions() + { + SaLogger.Info($"{SysUtil.Fn} START", true); + DateTime startedAt = DateTime.Now; + ClearProcessMessages(); + + int courseCount = 0; + int maxCourses = 0; + int courseCaptionsCorrected = 0; + + // Verify Course captions. + using (DisposableCollection disposables = new DisposableCollection()) + { + // Local function to allow break/return from within nested loop. + void CompareCourseCaptions() + { + // Countries. + foreach (SaxGolfUsgaCountry country in ReferenceGolfMgr.GetUsgaCountryList(new SaxGolfUsgaCountry.Params())) + { + disposables.Add(country); + + // Provinces. + SaxGolfUsgaProvince.Params prmProvince = new SaxGolfUsgaProvince.Params(); + prmProvince.CountryIdsIncluded.Add(country.Id); + foreach (SaxGolfUsgaProvince province in ReferenceGolfMgr.GetUsgaProvinceList(prmProvince)) + { + disposables.Add(province); + + // Separate disposables for each province to restrict volume handled on each disposal. + using (DisposableCollection disposablesProvince = new DisposableCollection()) + { + + // Facilities. + SaxGolfUsgaFacility.Params prmFacility = new SaxGolfUsgaFacility.Params(); + prmFacility.ProvinceIdsIncluded.Add(province.Id); + foreach (SaxGolfUsgaFacility facility in ReferenceGolfMgr.GetUsgaFacilityList(prmFacility)) + { + disposablesProvince.Add(facility); + + // Courses. + SaxGolfUsgaCourse.Params prmCourse = new SaxGolfUsgaCourse.Params(); + prmCourse.FacilityIdsIncluded.Add(facility.Id); + foreach (SaxGolfUsgaCourse course in ReferenceGolfMgr.GetUsgaCourseList(prmCourse)) + { + disposablesProvince.Add(course); + courseCount++; + + // Compare course caption. + String courseCaption = GetCourseCaption(facility.FacilityName, course.CourseName); + if (!course.Caption.Equals(courseCaption)) + { + course.Caption = courseCaption; + if (ReferenceGolfMgr.UpdateUsgaCourse(course, DbUpdateInfo)) + { + courseCaptionsCorrected++; + SaLogger.Info($"{SysUtil.Fn} Course caption corrected. Found: {course.Caption}.Corrected: {courseCaption}."); + } + else + { + SaLogger.Warn($"{SysUtil.Fn} Error updating Course caption: {ReferenceGolfMgr.ErrorStateMessage}"); + return; + } + } + + if (maxCourses > 0 && courseCount > maxCourses) + { + SaLogger.Info($"{SysUtil.Fn} Maximum number of courses processed ({maxCourses})."); + return; + } + } + } + } + } + } + } + // Invoke local function. + CompareCourseCaptions(); + } + + // Log statistics. + SaLogger.Info($"{SysUtil.Fn} Course caption corrected: {courseCaptionsCorrected}. Courses checked: {courseCount}.", true); + + SaLogger.Info($"{SysUtil.Fn} END Elapsed: {DateUtil.FormatNumberOfSeconds((DateTime.Now - startedAt).TotalSeconds)}", true); + return OK; + } + //------------------------------------------------------------------ /// /// Identify the column for each marker property. diff --git a/SaxoniteCoreWebAdmin/Admin/Consoles/SaxProcessConsole_Admin.cs b/SaxoniteCoreWebAdmin/Admin/Consoles/SaxProcessConsole_Admin.cs index 204d147..d22a405 100644 --- a/SaxoniteCoreWebAdmin/Admin/Consoles/SaxProcessConsole_Admin.cs +++ b/SaxoniteCoreWebAdmin/Admin/Consoles/SaxProcessConsole_Admin.cs @@ -2,6 +2,7 @@ using Sarand.Net.Standard.Base.Core; using Sarand.Net.Standard.Base.Procs; using Saxonite.Net.Core.Web.Admin.Procs; +using Saxonite.Net.Core.Web.Base.Procs; using Saxonite.Net.Standard.Base.App; using Saxonite.Net.Standard.Base.Consoles; using Saxonite.Net.Standard.Base.Procs; @@ -18,19 +19,20 @@ namespace Saxonite.Net.Core.Web.Admin.Consoles // Definitions. public enum ProcessType { - OrganisationUpload + OrganisationUpload, + GolfUsgaDataLookup } //------------------------------------------------------------------- // Process cache - to sustain processes outside of request/response timescales. internal class ProcessData { - public ProcessData(SaxProcess_Admin process) + public ProcessData(SaxProcess_Web process) { this.Process = process; } - public SaxProcess_Admin Process { get; } + public SaxProcess_Web Process { get; } } // Cache. private Dictionary processes { get; } = new Dictionary(); @@ -62,14 +64,14 @@ namespace Saxonite.Net.Core.Web.Admin.Consoles // Processes. //------------------------------------------------------------------- - public SaxProcess_Admin GetProcess(ProcessType processType) + public SaxProcess_Web GetProcess(ProcessType processType) { return processes.GetValueOrDefault(processType, null)?.Process; } public bool IsProcessing(ProcessType processType) { - SaxProcess_Admin process = GetProcess(processType); + SaxProcess_Web process = GetProcess(processType); if (process != null) if (process.Processing) return true; return false; @@ -83,7 +85,7 @@ namespace Saxonite.Net.Core.Web.Admin.Consoles /// public bool ClearProcess(ProcessType processType) { - SaxProcess_Admin process = GetProcess(processType); + SaxProcess_Web process = GetProcess(processType); if (process != null) { if (process.Processing) @@ -93,7 +95,7 @@ namespace Saxonite.Net.Core.Web.Admin.Consoles return processes.Remove(processType); } - public void SetProcess(ProcessType processType, SaxProcess_Admin process) + public void SetProcess(ProcessType processType, SaxProcess_Web process) { ClearProcess(processType); processes.Add(processType, new ProcessData(process)); @@ -105,7 +107,7 @@ namespace Saxonite.Net.Core.Web.Admin.Consoles //------------------------------------------------------------------- // Organisation Upload. - public SaxOrganisationUploader GetSaxOrganisationUploader() + public SaxOrganisationUploader GetOrganisationUploader() { return GetProcess(ProcessType.OrganisationUpload) as SaxOrganisationUploader; } @@ -114,6 +116,17 @@ namespace Saxonite.Net.Core.Web.Admin.Consoles SetProcess(ProcessType.OrganisationUpload, process); } + //------------------------------------------------------------------- + // Organisation Upload. + public SaxGolfUsgaDataLookup GetGolfUsgaDataLookup() + { + return GetProcess(ProcessType.GolfUsgaDataLookup) as SaxGolfUsgaDataLookup; + } + public void SetProcess(SaxGolfUsgaDataLookup process) + { + SetProcess(ProcessType.OrganisationUpload, process); + } + //------------------------------------------------------------------- } //======================================================================= diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/Reference/ReferenceGolfController-Ajax-Stats.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/Reference/ReferenceGolfController-Ajax-Stats.cs index a746a54..1d3a4a1 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/Reference/ReferenceGolfController-Ajax-Stats.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/Reference/ReferenceGolfController-Ajax-Stats.cs @@ -5,6 +5,7 @@ using Sarand.Net.Standard.Base.Core; using Sarand.Net.Standard.Base.Dbas; using Sarand.Net.Standard.Base.Utils; using Saxonite.Net.Core.Web.Admin.Models; +using Saxonite.Net.Core.Web.Admin.Models.Reference; using Saxonite.Net.Standard.Base.App; using Saxonite.Net.Standard.Base.Bus; using Saxonite.Net.Standard.Base.Bus.Reference; @@ -15,10 +16,10 @@ using System.Threading.Tasks; namespace Saxonite.Net.Core.Web.Admin.Controllers.Reference { - //======================================================================= - // Reference Golf Controller - Ajax requests. - //======================================================================= - partial class ReferenceGolfController + //======================================================================= + // Reference Golf Controller - Ajax requests. + //======================================================================= + partial class ReferenceGolfController { //------------------------------------------------------------------- // Get Stats ajax request. diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/Reference/ReferenceGolfController-Ajax.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/Reference/ReferenceGolfController-Ajax.cs index 96eab0f..e21be6a 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/Reference/ReferenceGolfController-Ajax.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/Reference/ReferenceGolfController-Ajax.cs @@ -1,10 +1,15 @@ using Microsoft.AspNetCore.Mvc; +using NPOI.SS.Formula.Functions; using Sarand.Net.Standard.Base.App; using Sarand.Net.Standard.Base.Bus; using Sarand.Net.Standard.Base.Core; using Sarand.Net.Standard.Base.Dbas; using Sarand.Net.Standard.Base.Utils; +using Saxonite.Net.Core.Web.Admin.Consoles; using Saxonite.Net.Core.Web.Admin.Models; +using Saxonite.Net.Core.Web.Admin.Models.Reference; +using Saxonite.Net.Core.Web.Admin.Procs; +using Saxonite.Net.Core.Web.Base.Procs; using Saxonite.Net.Standard.Base.App; using Saxonite.Net.Standard.Base.Bus; using Saxonite.Net.Standard.Base.Bus.Reference; @@ -115,6 +120,46 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers.Reference PopulateUsgaCourseResponseListData(course, data); } + //------------------------------------------------------------------- + // USGA Marker data. + private class UsgaMarkerListData : AjaxResponse_Data.ItemData_Base + { + public long? UsgaMarkerId { get; set; } + public String Caption { get; set; } + public short? Holes { get; set; } + public String HolesCaption { get; set; } + public String Tee { get; set; } + public String Gender { get; set; } + public short? SlopeRating { get; set; } + public short? Par { get; set; } + public decimal? CourseRating { get; set; } + public decimal? BogeyRating { get; set; } + public short? Length { get; set; } + } + private class UsgaMarkerItemData : UsgaMarkerListData + { + } + private void PopulateUsgaMarkerResponseListData(SaxGolfUsgaMarker marker, UsgaMarkerListData data) + { + data.Id = SysId.ToString(marker.Id); + data.Caption = marker.Caption; + data.Holes = marker.Holes; + data.HolesCaption = marker.HolesCaption; + data.Tee = marker.Tee; + data.Gender = marker.Gender; + data.SlopeRating = marker.SlopeRating; + data.Par = marker.Par; + data.Gender = marker.Gender; + data.BogeyRating = marker.BogeyRating; + data.Length = marker.Length; + data.StatusCode = marker.StatusCode; + data.StatusDesc = marker.StatusDesc; + } + private void PopulateUsgaMarkerResponseItemData(SaxGolfUsgaMarker course, UsgaMarkerItemData data) + { + PopulateUsgaMarkerResponseListData(course, data); + } + //------------------------------------------------------------------- // Get Country List ajax request. //------------------------------------------------------------------- @@ -133,7 +178,6 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers.Reference [Route("Reference/Golf/Ajax/GetCountries")] public IActionResult Ajax_GetCountryList(Model_ReferenceGolf model) { - String fn = GetType().Name + ".Ajax_GetCountryList"; String messageTitle = Localiser["caption.reference.golf"]; AjaxResponse_ReferenceGolf_CountryList responseData = new AjaxResponse_ReferenceGolf_CountryList(model); @@ -218,7 +262,6 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers.Reference [Route("Reference/Golf/Ajax/GetProvinces")] public IActionResult Ajax_GetProvinceList(Model_ReferenceGolf model) { - String fn = GetType().Name + ".Ajax_GetProvinceList"; String messageTitle = Localiser["caption.reference.golf"]; AjaxResponse_ReferenceGolf_ProvinceList responseData = new AjaxResponse_ReferenceGolf_ProvinceList(model); @@ -305,7 +348,6 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers.Reference [Route("Reference/Golf/Ajax/GetCourses")] public IActionResult Ajax_GetCourseList(Model_ReferenceGolf model) { - String fn = GetType().Name + ".Ajax_GetCourseList"; String messageTitle = Localiser["caption.reference.golf"]; AjaxResponse_ReferenceGolf_CourseList responseData = new AjaxResponse_ReferenceGolf_CourseList(model); @@ -384,6 +426,164 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers.Reference return Json(responseData); } + //------------------------------------------------------------------- + // Get Marker List ajax request. + //------------------------------------------------------------------- + private class AjaxResponse_ReferenceGolf_CleanseUsgaCourseCaptions : AjaxResponse_Data + { + // Construction. + public AjaxResponse_ReferenceGolf_CleanseUsgaCourseCaptions(SaxModel_Admin model) : base(model) { } + } + //------------------------------------------------------------------- + [HttpPost] + [Route("Reference/Golf/Ajax/CleanseUsgaCourseCaptions")] + public IActionResult Ajax_CleanseUsgaCourseCaptions(Model_ReferenceGolf model) + { + String messageTitle = Localiser["caption.reference.golf"]; + AjaxResponse_ReferenceGolf_CleanseUsgaCourseCaptions responseData = new AjaxResponse_ReferenceGolf_CleanseUsgaCourseCaptions(model); + + // Validate session. + if (!ValidUserSession) + return GetAjaxResult_InvalidSession(responseData); + + // Record disposable items as they are created so all can be disposed when needed. + using (DisposableCollection disposableItems = new DisposableCollection()) + { + + // Process console. + SaxProcessConsole_Admin processConsole = GetProcessConsole_Admin(SessionContext); + if (processConsole == null) + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); + + try + { + + // Check whether process is in progress. + if (processConsole.IsProcessing(SaxProcessConsole_Admin.ProcessType.GolfUsgaDataLookup)) + return Json(responseData.SetMessage(messageTitle, Localiser["msg.process.in.progress"])); + + // Configure the process. + SaxGolfUsgaDataLookup.Config processConfig = new SaxGolfUsgaDataLookup.Config(SessionContext, DbUpdateInfo); + processConfig.Tasks.Add(SaxGolfUsgaDataLookup.Task.CleanseCourseCaptions); + SaxGolfUsgaDataLookup process = new SaxGolfUsgaDataLookup(processConfig); + + // Store the process and run. + processConsole.SetProcess(process); + process.RunAsync(); + + } + catch (Exception exc) + { + SaLogger.Warn($"{SysUtil.Fn} {exc.GetType().Name} initialising organisation uploader."); + return Json(responseData.SetMessage(messageTitle, exc.GetType().Name + " initialising USGA Golf Data Loader.")); + } + } + + // Complete response. + Json(responseData.SetMessage(messageTitle, "Course caption cleansing is underway.")); + responseData.Success = true; + return Json(responseData); + } + + //------------------------------------------------------------------- + // Get Marker List ajax request. + //------------------------------------------------------------------- + private class AjaxResponse_ReferenceGolf_MarkerList : AjaxResponse_Data + { + // Construction. + public AjaxResponse_ReferenceGolf_MarkerList(SaxModel_Admin model) : base(model) { } + // Marker list. + public List Markers { get; } = new List(); + } + //------------------------------------------------------------------- + [HttpPost] + [Route("Reference/Golf/Ajax/GetUsgaMarkerList")] + [Route("Reference/Golf/Ajax/GetUsgaMarkers")] + [Route("Reference/Golf/Ajax/GetMarkerList")] + [Route("Reference/Golf/Ajax/GetMarkers")] + public IActionResult Ajax_GetMarkerList(Model_ReferenceGolf model) + { + String messageTitle = Localiser["caption.reference.golf"]; + AjaxResponse_ReferenceGolf_MarkerList responseData = new AjaxResponse_ReferenceGolf_MarkerList(model); + + // Validate session. + if (!ValidUserSession) + return GetAjaxResult_InvalidSession(responseData); + + // Item columns. + ColumnDfn colId = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.Id); + ColumnDfn colUsgaMarkerId = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.UsgaMarkerId); + ColumnDfn colCaption = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.Caption); + ColumnDfn colHoles = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.Holes); + ColumnDfn colHolesCaption = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.HolesCaption); + ColumnDfn colTee = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.Tee); + ColumnDfn colGender = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.Gender); + ColumnDfn colSlopeRating = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.SlopeRating); + ColumnDfn colPar = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.Par); + ColumnDfn colCourseRating = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.CourseRating); + ColumnDfn colBogeyRating = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.BogeyRating); + ColumnDfn colLength = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.Length); + ColumnDfn colFlags = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.Flags); + ColumnDfn colStatus = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.Status); + List columns = new List() + { + colId, colUsgaMarkerId, colCaption, colHoles, colHolesCaption, colTee, colGender, colSlopeRating, colPar, colCourseRating, colBogeyRating, colLength, colFlags, colStatus + }; + + // Item filter. + SaxGolfUsgaMarker.Params prmUsgaMarker = new SaxGolfUsgaMarker.Params(); + if (StringUtil.ToLong(model.Filter.CourseId) > 0) + prmUsgaMarker.CourseIdsIncluded.Add(StringUtil.ToSysId(model.Filter.CourseId)); + prmUsgaMarker.MarkerFilter = model.Filter.MarkerSearch; + prmUsgaMarker.StatusCat = SaxGolfUsgaMarker.StatusCat.Any; + model.Paging.ItemTotal = (ulong)ReferenceGolfMgr.GetUsgaMarkerCount(prmUsgaMarker); + model.PopulatePagingTo(prmUsgaMarker.ResultsPaging); + // Restrict results is not paged. + if (!prmUsgaMarker.ResultsPaging.Paged) + { + prmUsgaMarker.ResultsPaging.PageNo = 1; + prmUsgaMarker.ResultsPaging.PageLength = 200; + } + + // Sort order. + prmUsgaMarker.SortColumns.Add(colCaption); + + // Query items. + using (DbResultSet resultSet = ReferenceGolfMgr.ExecuteUsgaMarkerQuery(columns, prmUsgaMarker)) + { + while (resultSet != null && resultSet.HasRows && resultSet.NextRecord()) + { + NamedValue.Map values = resultSet.GetRecordValues(); + ushort flags = (ushort)values.GetLong(colFlags.DataName); + UsgaMarkerListData markerData = new UsgaMarkerListData() + { + Id = values.GetString(colId.DataName), + UsgaMarkerId = values.GetLong(colUsgaMarkerId.DataName), + Caption = values.GetString(colCaption.DataName), + Holes = (short)values.GetLong(colHoles.DataName), + HolesCaption = values.GetString(colHolesCaption.DataName), + Tee = values.GetString(colTee.DataName), + Gender = values.GetString(colGender.DataName), + SlopeRating = (short)values.GetLong(colSlopeRating.DataName), + Par = (short)values.GetLong(colPar.DataName), + CourseRating = values.GetDecimal(colCourseRating.DataName), + BogeyRating = values.GetDecimal(colBogeyRating.DataName), + Length = (short)values.GetLong(colLength.DataName), + StatusCode = values.GetString(colStatus.DataName), + StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName)) + }; + responseData.Markers.Add(markerData); + } + } + if (model.Paging.IsPaged) + model.Paging.ItemCount = (ushort)responseData.Markers.Count; + + // Complete response. + responseData.SetPaging(model); + responseData.Success = true; + return Json(responseData); + } + //------------------------------------------------------------------- } //======================================================================= diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/Reference/ReferenceGolfController.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/Reference/ReferenceGolfController.cs index 87c9f8f..e337475 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/Reference/ReferenceGolfController.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/Reference/ReferenceGolfController.cs @@ -1,6 +1,6 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; -using Saxonite.Net.Core.Web.Admin.Models; +using Saxonite.Net.Core.Web.Admin.Models.Reference; using Saxonite.Net.Core.Web.Base.App; using Saxonite.Net.Core.Web.Base.Controllers; using System; @@ -11,8 +11,8 @@ using System.Threading.Tasks; namespace Saxonite.Net.Core.Web.Admin.Controllers.Reference { - //======================================================================= - public partial class ReferenceGolfController : SaxController_Admin + //======================================================================= + public partial class ReferenceGolfController : SaxController_Admin { //------------------------------------------------------------------- // Construction. diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/System/SystemController-Ajax-Logs.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/System/SystemController-Ajax-Logs.cs index 5f45b20..d64b55e 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/System/SystemController-Ajax-Logs.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/System/SystemController-Ajax-Logs.cs @@ -32,6 +32,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers.System private class LogFileData { public String Name { get; set; } + public String UpdateFmt { get; set; } public long Size { get; set; } } @@ -207,7 +208,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers.System DirectoryInfo folder = new DirectoryInfo(logFolder); foreach (FileInfo file in folder.EnumerateFiles()) { - responseData.Files.Add(new LogFileData() { Name = file.Name, Size = file.Length }); + responseData.Files.Add(new LogFileData() { Name = file.Name, UpdateFmt = file.LastWriteTime.ToString("d-MMM-yy HH:mm"), Size = file.Length }); } // Complete response. diff --git a/SaxoniteCoreWebAdmin/Admin/Models/Model_ReferenceGolf.cs b/SaxoniteCoreWebAdmin/Admin/Models/Reference/Model_ReferenceGolf.cs similarity index 75% rename from SaxoniteCoreWebAdmin/Admin/Models/Model_ReferenceGolf.cs rename to SaxoniteCoreWebAdmin/Admin/Models/Reference/Model_ReferenceGolf.cs index ee9aa57..e2433e8 100644 --- a/SaxoniteCoreWebAdmin/Admin/Models/Model_ReferenceGolf.cs +++ b/SaxoniteCoreWebAdmin/Admin/Models/Reference/Model_ReferenceGolf.cs @@ -13,7 +13,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -namespace Saxonite.Net.Core.Web.Admin.Models +namespace Saxonite.Net.Core.Web.Admin.Models.Reference { //======================================================================= public class Model_ReferenceGolf : SaxModel_Admin @@ -30,12 +30,15 @@ namespace Saxonite.Net.Core.Web.Admin.Models // Reference Golf list parameters. public class FilterData { - public String CountrySearch { get; set; } - public String CountryId { get; set; } - public String ProvinceSearch { get; set; } - public String ProvinceId { get; set; } - public String CourseSearch{ get; set; } - public String CourseId { get; set; } + public string CountrySearch { get; set; } + public string CountryId { get; set; } + public string ProvinceSearch { get; set; } + public string ProvinceId { get; set; } + public string CourseSearch { get; set; } + public string CourseId { get; set; } + public string MarkerSearch { get; set; } + public string MarkerHoles { get; set; } + public string MarkerGender { get; set; } } public FilterData Filter { get; } = new FilterData(); @@ -46,7 +49,7 @@ namespace Saxonite.Net.Core.Web.Admin.Models // Validation. //------------------------------------------------------------------- // Reference Golf. - public bool ValidateReferenceGolfData(SaxController_Base.AjaxResponse_Data responseData) + public bool ValidateReferenceGolfData(Sacs.Web.Components.SaController.AjaxResponse_Data responseData) { // Record disposable items as they are created so all can be disposed when needed. using (DisposableCollection disposableItems = new DisposableCollection()) diff --git a/SaxoniteCoreWebAdmin/Localisation/SaxAdminProperties.resx b/SaxoniteCoreWebAdmin/Localisation/SaxAdminProperties.resx index a334b55..925e71f 100644 --- a/SaxoniteCoreWebAdmin/Localisation/SaxAdminProperties.resx +++ b/SaxoniteCoreWebAdmin/Localisation/SaxAdminProperties.resx @@ -258,6 +258,9 @@ Awaiting activation + + Bogey + Cancel @@ -345,6 +348,9 @@ Facilities + + File + Filename @@ -366,9 +372,15 @@ Full name + + Gender + Header row + + Holes + Image @@ -408,6 +420,9 @@ Last name + + Length + Location @@ -480,6 +495,9 @@ Page {0} of {1} + + Par + Parent @@ -555,6 +573,12 @@ Sheet name or number (from 1) + + Size + + + Slope + Speciality @@ -585,6 +609,9 @@ Table + + Tee + Title @@ -597,6 +624,9 @@ update + + Updated + Upload diff --git a/SaxoniteCoreWebAdmin/Views/Home/Index.cshtml b/SaxoniteCoreWebAdmin/Views/Home/Index.cshtml index c0cc304..65fc94d 100644 --- a/SaxoniteCoreWebAdmin/Views/Home/Index.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Home/Index.cshtml @@ -72,7 +72,7 @@
@Localiser["system.caption.database.core"]
-
+
@@ -85,7 +85,7 @@
@Localiser["system.caption.database.reference"]
-
+
@@ -98,7 +98,7 @@
@Localiser["system.caption.database.support"]
-
+
diff --git a/SaxoniteCoreWebAdmin/Views/Reference/Golf/Index.cshtml b/SaxoniteCoreWebAdmin/Views/Reference/Golf/Index.cshtml index b100bba..6442e75 100644 --- a/SaxoniteCoreWebAdmin/Views/Reference/Golf/Index.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Reference/Golf/Index.cshtml @@ -30,23 +30,24 @@
@(Localiser[
+ @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmRefGolfFilter", @class = "sax-form", @onSubmit = "return false;" })) + { + @Html.HiddenFor(model => model.Filter.CountrySearch, new { @class = "FilterCountrySearch" }) + @Html.HiddenFor(model => model.Filter.CountryId, new { @class = "FilterCountryId" }) + @Html.HiddenFor(model => model.Filter.ProvinceSearch, new { @class = "FilterProvinceSearch" }) + @Html.HiddenFor(model => model.Filter.ProvinceId, new { @class = "FilterProvinceId" }) + @Html.HiddenFor(model => model.Filter.CourseSearch, new { @class = "FilterCourseSearch" }) + @Html.HiddenFor(model => model.Filter.CourseId, new { @class = "FilterCourseId" }) + @*
- @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmRefGolfFilter", @class = "sax-form", @onSubmit = "return false;" })) - { - @Html.HiddenFor(model => model.Filter.CountrySearch, new { @class = "FilterCountrySearch" }) - @Html.HiddenFor(model => model.Filter.CountryId, new { @class = "FilterCountryId" }) - @Html.HiddenFor(model => model.Filter.ProvinceSearch, new { @class = "FilterProvinceSearch" }) - @Html.HiddenFor(model => model.Filter.ProvinceId, new { @class = "FilterProvinceId" }) - @Html.HiddenFor(model => model.Filter.CourseSearch, new { @class = "FilterCourseSearch" }) - @Html.HiddenFor(model => model.Filter.CourseId, new { @class = "FilterCourseId" }) -
-
@Localiser["label.options"]
-
- @* ToDo ... *@ -
-
- } +
+
@Localiser["label.options"]
+
+
+
+ *@ + }
@* Reference - Golf - Stats *@ @@ -63,8 +64,6 @@
- -
@@ -88,8 +87,8 @@
+
-
@@ -105,17 +104,19 @@
- +
+
- - - - - - - + + + + + + +
+
} @@ -134,8 +135,8 @@
+
-
@@ -151,17 +152,19 @@
- +
+
- - - - - - - + + + + + + +
+ } @@ -177,11 +180,21 @@
@Localiser["label.courses"]
-
+
+ @* Burger menu *@ + +
+
-
@@ -196,30 +209,87 @@
- - - - - - - - +
+ @* Course template - table is ignored. *@ +
+ + + + + + + + +
+ @* Burger dropdown item template - ul is ignored *@ +
    +
  • ...
  • +
+ + + } + + @* Reference - Golf - Markers *@ + @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmMarkers", @class = "sax-form", @onSubmit = "return false;" })) + { + @Html.HiddenFor(Model => Model.Filter.CourseId, new { @class = "FilterCourseId" }) + @Html.HiddenFor(Model => Model.Filter.MarkerHoles, new { @class = "FilterMarkerHoles" }) + @Html.HiddenFor(Model => Model.Filter.MarkerGender, new { @class = "FilterMarkerGender" }) +
+
+
+
+ @Localiser["label.search"] + +
+
@Localiser["label.markers"]
+
+
+
+
+
+ + + + + + + + + + + + + + + + +
@Localiser["label.caption"]@Localiser["label.holes"]@Localiser["label.tee"]@Localiser["label.gender"]@Localiser["label.slope"]@Localiser["label.par"]@Localiser["label.course"]@Localiser["label.bogey"]@Localiser["label.length"]@Localiser["label.status"]
+
+
+ + + + + + + + + + + +
+
} @* Icons. *@
-
-
Active -
-
Inactive -
-
Deleted
diff --git a/SaxoniteCoreWebAdmin/Views/System/Logs.cshtml b/SaxoniteCoreWebAdmin/Views/System/Logs.cshtml index e19e808..915175a 100644 --- a/SaxoniteCoreWebAdmin/Views/System/Logs.cshtml +++ b/SaxoniteCoreWebAdmin/Views/System/Logs.cshtml @@ -53,21 +53,23 @@ @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmLogsFilter", @class = "sax-form", @onSubmit = "return false;" })) { -
-
@Localiser["label.root.folder"]
-
@(Model.LoggingRootFolder)
-
-
-
@Localiser["label.folder"]
-
-
-
-
@Localiser["label.year"]
-
-
-
@Localiser["label.month"]
-
+
+
@Localiser["label.root.folder"]
+
@(Model.LoggingRootFolder)
+
+
+
@Localiser["label.folder"]
+
+
+
+
@Localiser["label.year"]
+
+
+
+
@Localiser["label.month"]
+
+
} @@ -75,20 +77,33 @@
-
-
-
-
-
-
-
-
-
-
+ @* Log file list. *@ +
+ + + + + + + + + +
@Localiser["label.file"]@Localiser["label.updated"]@Localiser["label.size"]
+ @* Log file template. *@ + + + + + + +
+
+ +
diff --git a/SaxoniteCoreWebAdmin/Views/_ViewImports.cshtml b/SaxoniteCoreWebAdmin/Views/_ViewImports.cshtml index 5ea8d6f..001f8b6 100644 --- a/SaxoniteCoreWebAdmin/Views/_ViewImports.cshtml +++ b/SaxoniteCoreWebAdmin/Views/_ViewImports.cshtml @@ -5,5 +5,6 @@ @using Saxonite.Net.Standard.Base.App @using Saxonite.Net.Core.Web.Base.App @using Saxonite.Net.Core.Web.Admin.Models +@using Saxonite.Net.Core.Web.Admin.Models.Reference @using Saxonite.Net.Core.Web.Admin.Models.Support @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/SaxoniteCoreWebAdmin/wwwroot/css/sax-data.css b/SaxoniteCoreWebAdmin/wwwroot/css/sax-data.css index b1873d6..f7d82e0 100644 --- a/SaxoniteCoreWebAdmin/wwwroot/css/sax-data.css +++ b/SaxoniteCoreWebAdmin/wwwroot/css/sax-data.css @@ -318,6 +318,11 @@ table.sax-item-list tbody tr.sax-sel background-color: palegreen; } +table.sax-item-list tbody td +{ + padding: 0 2px; +} + /* Item */ .sax-panel-item-template diff --git a/SaxoniteCoreWebAdmin/wwwroot/css/sax-main.css b/SaxoniteCoreWebAdmin/wwwroot/css/sax-main.css index f9ce95d..606aa9f 100644 --- a/SaxoniteCoreWebAdmin/wwwroot/css/sax-main.css +++ b/SaxoniteCoreWebAdmin/wwwroot/css/sax-main.css @@ -127,7 +127,7 @@ button.accept-policy #sax-page-footer div, #sax-page-footer a { - font-size:0.75rem; + font-size: 0.75rem; } /* Information */ @@ -383,14 +383,21 @@ a:focus .ui-dialog .ui-dialog-buttonpane button { - min-width:80px; - margin:4px 8px 4px 0; + min-width: 80px; + margin: 4px 8px 4px 0; color: #fff; background-color: #d4af37; border-color: #d4af37; outline: none; } +.ui-dialog .ui-dialog-buttonpane button:disabled +{ + color: #e0e0e0; + background-color:#d5be70; + border-color:#d5be70; +} + .ui-widget-overlay { background-color: #b0b0b0; diff --git a/SaxoniteCoreWebAdmin/wwwroot/js/sax-ref-golf.js b/SaxoniteCoreWebAdmin/wwwroot/js/sax-ref-golf.js index 52301cf..76df989 100644 --- a/SaxoniteCoreWebAdmin/wwwroot/js/sax-ref-golf.js +++ b/SaxoniteCoreWebAdmin/wwwroot/js/sax-ref-golf.js @@ -5,7 +5,7 @@ (function (win, doc, undef) { "use strict"; - let rg = sax.refGolf = sax.refGolf || { data: {}, fn: {}, $pnl: {}, $frm: {}, $dlg: {} }; + let rg = sax.refGolf = sax.refGolf || { ajax: {}, data: {}, fn: {}, $pnl: {}, $frm: {}, $dlg: {} }; rg.attr_id = 'sax-id'; rg.class_sel = 'sax-sel'; @@ -13,8 +13,8 @@ /* Reference - Golf - Stats. */ rg.fn.refreshStats = function (delay) { - rg.ajaxGolfStats = rg.ajaxGolfStats || { url: sax.url.base + 'Reference/Golf/Ajax/GetStats', formId: rg.$frm.filter.prop('id'), callbackFn: rg.fn.doRefreshStats }; - sax.doAjaxRequest(rg.ajaxGolfStats, delay || 50); + rg.ajax.golfStats = rg.ajax.golfStats || { url: sax.url.base + 'Reference/Golf/Ajax/GetStats', formId: rg.$frm.filter.prop('id'), callbackFn: rg.fn.doRefreshStats }; + sax.doAjaxRequest(rg.ajax.golfStats, delay || 50); } rg.fn.doRefreshStats = function (response) { @@ -64,8 +64,8 @@ rg.fn.refreshCountries = function (delay) { rg.fn.clearCountries(); - rg.ajaxGolfCountries = rg.ajaxGolfCountries || { url: sax.url.base + 'Reference/Golf/Ajax/GetCountries', formId: rg.$frm.countries.prop('id'), callbackFn: rg.fn.doRefreshCountries }; - sax.doAjaxRequest(rg.ajaxGolfCountries, delay || 50); + rg.ajax.golfCountries = rg.ajax.golfCountries || { url: sax.url.base + 'Reference/Golf/Ajax/GetCountries', formId: rg.$frm.countries.prop('id'), callbackFn: rg.fn.doRefreshCountries }; + sax.doAjaxRequest(rg.ajax.golfCountries, delay || 50); } rg.fn.doRefreshCountries = function (response) { @@ -97,14 +97,14 @@ } rg.fn.getCountryTemplate = function () { - return $('table.sax-panel-item-template tr.GolfCountry', rg.$pnl.countries).clone(); + return $('.sax-panel-item-template tr.GolfCountry', rg.$pnl.countries).clone(); } rg.fn.selectCountry = function ($item, item) { //sax.debug('selectCountry', $item, item) $('table.sax-item-list tbody tr', rg.$pnl.countries).removeClass(rg.class_sel); $item.addClass(rg.class_sel); - setTimeout(rg.fn.refreshProvinces(500), 100); + setTimeout(function () { rg.fn.refreshProvinces(250); }, 50); } //-------------------------------------------------------------- @@ -126,8 +126,8 @@ return; } $('.FilterCountryId', rg.$frm.provinces).val(countryId); - rg.ajaxGolfProvinces = rg.ajaxGolfProvinces || { url: sax.url.base + 'Reference/Golf/Ajax/GetProvinces', formId: rg.$frm.provinces.prop('id'), callbackFn: rg.fn.doRefreshProvinces }; - sax.doAjaxRequest(rg.ajaxGolfProvinces, delay || 50); + rg.ajax.golfProvinces = rg.ajax.golfProvinces || { url: sax.url.base + 'Reference/Golf/Ajax/GetProvinces', formId: rg.$frm.provinces.prop('id'), callbackFn: rg.fn.doRefreshProvinces }; + sax.doAjaxRequest(rg.ajax.golfProvinces, delay || 50); } rg.fn.doRefreshProvinces = function (response) { @@ -137,7 +137,13 @@ let $list = rg.$pnl.provincesList; $('tbody tr', $list).remove() for (let provinceIdx = 0; provinceIdx < (response.provinces || []).length; provinceIdx++) - $list.append(rg.fn.populateProvince(rg.fn.getProvinceTemplate(), response.provinces[provinceIdx])); + { + let $item = rg.fn.getProvinceTemplate(); + let item = response.provinces[provinceIdx]; + $list.append(rg.fn.populateProvince($item, item)); + if (provinceIdx == 0 && response.provinces.length == 1) + rg.fn.selectProvince($item, item); + } } rg.fn.populateProvince = function ($item, item) { @@ -153,14 +159,14 @@ } rg.fn.getProvinceTemplate = function () { - return $('table.sax-panel-item-template tr.GolfProvince', rg.$pnl.provinces).clone(); + return $('.sax-panel-item-template tr.GolfProvince', rg.$pnl.provinces).clone(); } rg.fn.selectProvince = function ($item, item) { //sax.debug('selectProvince', $item, item) $('table.sax-item-list tbody tr', rg.$pnl.provinces).removeClass(rg.class_sel); $item.addClass(rg.class_sel); - setTimeout(rg.fn.refreshCourses(500), 100); + setTimeout(function () { rg.fn.refreshCourses(250); }, 50); } //-------------------------------------------------------------- @@ -180,8 +186,8 @@ return; } $('.FilterProvinceId', rg.$frm.courses).val(provinceId); - rg.ajaxGolfCourses = rg.ajaxGolfCourses || { url: sax.url.base + 'Reference/Golf/Ajax/GetCourses', formId: rg.$frm.courses.prop('id'), callbackFn: rg.fn.doRefreshCourses }; - sax.doAjaxRequest(rg.ajaxGolfCourses, delay || 50); + rg.ajax.golfCourses = rg.ajax.golfCourses || { url: sax.url.base + 'Reference/Golf/Ajax/GetCourses', formId: rg.$frm.courses.prop('id'), callbackFn: rg.fn.doRefreshCourses }; + sax.doAjaxRequest(rg.ajax.golfCourses, delay || 50); } rg.fn.doRefreshCourses = function (response) { @@ -191,14 +197,20 @@ let $list = rg.$pnl.coursesList; $('tbody tr', $list).remove() for (let courseIdx = 0; courseIdx < (response.courses || []).length; courseIdx++) - $list.append(rg.fn.populateCourse(rg.fn.getCourseTemplate(), response.courses[courseIdx])); + { + let $item = rg.fn.getCourseTemplate(); + let item = response.courses[courseIdx]; + $list.append(rg.fn.populateCourse($item, item)); + if (courseIdx == 0 && response.courses.length == 1) + rg.fn.selectCourse($item, item); + } } rg.fn.populateCourse = function ($item, item) { $item.attr(rg.attr_id, item.id).on('click', function () { rg.fn.selectCourse($item, item); }) $('.CourseId', $item).text(item.usgaCourseId); $('.CourseCaption', $item).text(item.caption); - $('.CourseLocation', $item).text(item.city||item.address2||item.address1); + $('.CourseLocation', $item).text(item.city || item.address2 || item.address1); if (item.markerCountActive) $('.CourseMarkers', $item).text(item.markerCountActive); if (item.priorityPopulate) $('.CoursePriority', $item).append(rg.fn.getTickIcon()); $('.CourseStatus', $item).append(rg.fn.getStatusIcon(item.statusCode.toLowerCase())); @@ -206,18 +218,101 @@ } rg.fn.getCourseTemplate = function () { - return $('table.sax-panel-item-template tr.GolfCourse', rg.$pnl.courses).clone(); + return $('.sax-panel-item-template tr.GolfCourse', rg.$pnl.courses).clone(); } rg.fn.selectCourse = function ($item, item) { //sax.debug('selectCourse', $item, item) $('table.sax-item-list tbody tr', rg.$pnl.courses).removeClass(rg.class_sel); $item.addClass(rg.class_sel); - //setTimeout(rg.fn.refreshMarkers(500), 100); + setTimeout(function () { rg.fn.refreshMarkers(250); }, 50); + } + + //-------------------------------------------------------------- + // Courses burger. + rg.fn.configureCoursesBurger = function () + { + var $bgr = $('.sax-burger', rg.$pnl.courses).hide(); + var $mnu = $('ul.sax-dropdown-menu', $bgr).empty(); + $mnu.append(rg.fn.getCoursesMenuOption('Cleanse captions', function () { rg.fn.cleanseCourseCaptions(); }, 'sax-process')); + $bgr.toggle(!!$('.sax-dropdown-item', $mnu).length); + } + rg.fn.getCoursesDropdownTemplate = function () + { + return $('.sax-panel-item-template .sax-dropdown-menu.Courses li.sax-dropdown-item', rg.$pnl.courses).clone(); + } + rg.fn.getCoursesMenuOption = function (lbl, fn, cls) + { + return rg.fn.getMenuOption(rg.fn.getCoursesDropdownTemplate(), lbl, fn, cls); + } + rg.fn.cleanseCourseCaptions = function () + { + rg.ajax.cleanseCourseCaptions = rg.ajax.cleanseCourseCaptions || { url: sax.url.base + 'Reference/Golf/Ajax/CleanseUsgaCourseCaptions', formId: rg.$frm.courses.prop('id') }; + sax.doAjaxRequest(rg.ajax.cleanseCourseCaptions, 200); + } + + //-------------------------------------------------------------- + /* Reference - Golf - Markers. */ + rg.fn.clearMarkers = function () + { + rg.$pnl.markersMsgs.empty(); + rg.$pnl.markersList.find('tbody tr').remove(); + } + rg.fn.refreshMarkers = function (delay) + { + rg.fn.clearMarkers(); + let courseId = rg.$pnl.coursesList.find('tbody tr.' + rg.class_sel).attr(rg.attr_id); + if (!courseId) + { + rg.$pnl.markersMsgs.empty().text('Course must be selected.'); + return; + } + $('.FilterCourseId', rg.$frm.markers).val(courseId); + rg.ajax.golfMarkers = rg.ajax.golfMarkers || { url: sax.url.base + 'Reference/Golf/Ajax/GetMarkers', formId: rg.$frm.markers.prop('id'), callbackFn: rg.fn.doRefreshMarkers }; + sax.doAjaxRequest(rg.ajax.golfMarkers, delay || 50); + } + rg.fn.doRefreshMarkers = function (response) + { + //sax.debug('doRefreshMarkers', response); + rg.fn.clearMarkers(); + if (!response.success) return; + let $list = rg.$pnl.markersList; + $('tbody tr', $list).remove() + for (let markerIdx = 0; markerIdx < (response.markers || []).length; markerIdx++) + $list.append(rg.fn.populateMarker(rg.fn.getMarkerTemplate(), response.markers[markerIdx])); + } + rg.fn.populateMarker = function ($item, item) + { + $item.attr(rg.attr_id, item.id).on('click', function () { rg.fn.selectMarker($item, item); }) + $('.MarkerCaption', $item).text(item.caption); + $('.MarkerHoles', $item).text(item.holes); + $('.MarkerTee', $item).text(item.tee); + $('.MarkerGender', $item).text(item.gender); + $('.MarkerSlope', $item).text(item.slopeRating); + $('.MarkerPar', $item).text(item.par); + $('.MarkerCourse', $item).text(item.courseRating); + if (item.bogeyRating) $('.MarkerBogey', $item).text(item.bogeyRating); + if (item.length) $('.MarkerLength', $item).text(item.length); + $('.MarkerStatus', $item).append(rg.fn.getStatusIcon(item.statusCode.toLowerCase())); + return $item; + } + rg.fn.getMarkerTemplate = function () + { + return $('.sax-panel-item-template tr.GolfMarker', rg.$pnl.markers).clone(); + } + rg.fn.selectMarker = function ($item, item) + { + //sax.debug('selectMarker', $item, item) + $('table.sax-item-list tbody tr', rg.$pnl.markers).removeClass(rg.class_sel); + $item.addClass(rg.class_sel); } //-------------------------------------------------------------- // Utilities. + rg.fn.getMenuOption = function ($opt, lbl, fn, cls) + { + return $opt.find('div.sax-label').text(lbl).end().on('click', fn).addClass(cls); + } rg.fn.getTickIcon = function () { return $('.sax-panel-item-template img.sax-tick', rg.$pnl.golf).clone(); @@ -241,31 +336,39 @@ // Stats. rg.$pnl.stats = $('.sax-panel-info.GolfStats', rg.$pnl.golf); - setTimeout(rg.fn.refreshStats, 250); + setTimeout(function () { rg.fn.refreshStats(); }, 250); // Countries. rg.$pnl.countries = $('.sax-panel-info.Countries', rg.$pnl.golf); rg.$pnl.countriesMsgs = $('.sax-panel-messages', rg.$pnl.countries); rg.$pnl.countriesList = $('table.sax-item-list', rg.$pnl.countries); rg.$frm.countries = $('#frmCountries', rg.$pnl.golf); - $('.FilterCountrySearch', rg.$frm.countries).on('input', function () { setTimeout(rg.fn.refreshCountries(500), 100); }); - setTimeout(rg.fn.refreshCountries, 1000); + $('.FilterCountrySearch', rg.$frm.countries).on('input', function () { setTimeout(function () { rg.fn.refreshCountries(500); }, 50); }); + setTimeout(function () { rg.fn.refreshCountries(); }, 1000); // Provinces. rg.$pnl.provinces = $('.sax-panel-info.Provinces', rg.$pnl.golf); rg.$pnl.provincesMsgs = $('.sax-panel-messages', rg.$pnl.provinces); rg.$pnl.provincesList = $('table.sax-item-list', rg.$pnl.provinces); rg.$frm.provinces = $('#frmProvinces', rg.$pnl.golf); - $('.FilterProvinceSearch', rg.$frm.provinces).on('input', function () { setTimeout(rg.fn.refreshProvinces(500), 100); }); + $('.FilterProvinceSearch', rg.$frm.provinces).on('input', function () { setTimeout(function () { rg.fn.refreshProvinces(500); }, 50); }); // Courses. rg.$pnl.courses = $('.sax-panel-info.Courses', rg.$pnl.golf); rg.$pnl.coursesMsgs = $('.sax-panel-messages', rg.$pnl.courses); rg.$pnl.coursesList = $('table.sax-item-list', rg.$pnl.courses); rg.$frm.courses = $('#frmCourses', rg.$pnl.golf); - $('.FilterCourseSearch', rg.$frm.courses).on('input', function () { setTimeout(rg.fn.refreshCourses(500), 100); }); + $('.FilterCourseSearch', rg.$frm.courses).on('input', function () { setTimeout(function () { rg.fn.refreshCourses(500); }, 50); }); //$('.PagingPageNo', rg.$dlg).val(1); //$('.PagingPageLength', rg.$dlg).val(16); + rg.fn.configureCoursesBurger(); + + // Markers. + rg.$pnl.markers = $('.sax-panel-info.Markers', rg.$pnl.golf); + rg.$pnl.markersMsgs = $('.sax-panel-messages', rg.$pnl.markers); + rg.$pnl.markersList = $('table.sax-item-list', rg.$pnl.markers); + rg.$frm.markers = $('#frmMarkers', rg.$pnl.golf); + $('.FilterMarkerSearch', rg.$frm.markers).on('input', function () { setTimeout(function () { rg.fn.refreshMarkers(500); }, 50); }); var opt = sax.dlgOpts(); //opt.open = function () { rg.fn.refreshOrganisations(200); }; diff --git a/SaxoniteCoreWebAdmin/wwwroot/js/sax-sys-logs.js b/SaxoniteCoreWebAdmin/wwwroot/js/sax-sys-logs.js index 9e8b764..c68ceb6 100644 --- a/SaxoniteCoreWebAdmin/wwwroot/js/sax-sys-logs.js +++ b/SaxoniteCoreWebAdmin/wwwroot/js/sax-sys-logs.js @@ -13,8 +13,8 @@ { if (!sl.data.awaitingRefreshFoldersResponse) { - var $items = $('.sax-panel-item-list', $pnl.results).fadeOut(200).empty(); - $pnl.msgs.hide().empty().append($('
' + sax.msg.dataQuerying + '
')).fadeIn(200); + sl.clearFiles(); + //$pnl.msgs.hide().empty().append($('
' + sax.msg.dataQuerying + '
')).fadeIn(200); } sax.ajax.refreshFolders = sax.ajax.refreshFolders || { url: sax.url.base + 'System/Ajax/Logs/GetFolders', formId: sl.$frm.filter.prop('id'), callbackFn: sl.doRefreshFolders }; sax.doAjaxRequest(sax.ajax.refreshFolders, delay || 50); @@ -57,8 +57,8 @@ } sl.clearFiles = function () { - $('.sax-panel-messages', $pnl.results).hide().empty(); - $('.sax-panel-item-list', $pnl.results).hide().empty(); + $pnl.msgs.hide().empty(); + $pnl.list.find('tbody tr').remove(); } //-------------------------------------------------------------- @@ -66,8 +66,8 @@ { if (!sl.data.awaitingRefreshYearsResponse) { - var $items = $('.sax-panel-item-list', $pnl.results).fadeOut(200).empty(); - $pnl.msgs.hide().empty().append($('
' + sax.msg.dataQuerying + '
')).fadeIn(200); + sl.clearFiles(); + //$pnl.msgs.hide().empty().append($('
' + sax.msg.dataQuerying + '
')).fadeIn(200); } sax.ajax.refreshYears = sax.ajax.refreshYears || { url: sax.url.base + 'System/Ajax/Logs/GetYears', formId: sl.$frm.filter.prop('id'), callbackFn: sl.doRefreshYears }; sax.doAjaxRequest(sax.ajax.refreshYears, delay || 50); @@ -97,8 +97,8 @@ { if (!sl.data.awaitingRefreshMonthsResponse) { - var $items = $('.sax-panel-item-list', $pnl.results).fadeOut(200).empty(); - $pnl.msgs.hide().empty().append($('
' + sax.msg.dataQuerying + '
')).fadeIn(200); + sl.clearFiles(); + //$pnl.msgs.hide().empty().append($('
' + sax.msg.dataQuerying + '
')).fadeIn(200); } sax.ajax.refreshMonths = sax.ajax.refreshMonths || { url: sax.url.base + 'System/Ajax/Logs/GetMonths', formId: sl.$frm.filter.prop('id'), callbackFn: sl.doRefreshMonths }; sax.doAjaxRequest(sax.ajax.refreshMonths, delay || 50); @@ -130,8 +130,8 @@ { if (!sl.data.awaitingRefreshFilesResponse) { - var $items = $('.sax-panel-item-list', $pnl.results).fadeOut(200).empty(); - $pnl.msgs.hide().empty().append($('
' + sax.msg.dataQuerying + '
')).fadeIn(200); + sl.clearFiles(); + //$pnl.msgs.hide().empty().append($('
' + sax.msg.dataQuerying + '
')).fadeIn(200); } sax.ajax.refreshFiles = sax.ajax.refreshFiles || { url: sax.url.base + 'System/Ajax/Logs/GetFiles', formId: sl.$frm.filter.prop('id'), callbackFn: sl.doRefreshFiles }; sax.doAjaxRequest(sax.ajax.refreshFiles, delay || 50); @@ -143,23 +143,24 @@ delete sl.data.awaitingRefreshFilesResponse; sl.clearFiles(); sax.refreshControlValues(response.controlValues); - $pnl.msgs.hide().empty(); - var $items = $('.sax-panel-item-list', $pnl.results).hide().empty(); if (!response.success) return; sax.data.logMonth = $ctrl.cbMonths.val(); // Files. var items = response.files; - if (!items.length) + sl.data.files = response.files; + if (!(items || []).length) return $pnl.msgs.append($('
' + sax.msg.dataNoneFound + '
')).fadeIn(500); for (let itemIdx = 0; itemIdx < items.length; itemIdx++) - $items.append(sl.populateItem(sl.getFileTemplate(), items[itemIdx])); - $items.fadeIn(50); + $pnl.list.append(sl.populateItem(sl.getFileTemplate(), items[itemIdx])); + $pnl.list.fadeIn(50); } sl.populateItem = function ($item, item) { if (!item) return $item; - $('.Filename', $item).text(item.name).off('click').on('click', function () { sl.getFile(item.name) }); - $('.Length', $item).text(sax.formatFileSize(item.size, 2)); + $item.off('click').on('click', function () { sl.showFile(item.name) }); + $('.Filename', $item).text(item.name); + $('.Updated', $item).text(item.updateFmt); + $('.Size', $item).text(sax.formatFileSize(item.size, 2)); return $item; } sl.getFileTemplate = function () @@ -168,29 +169,90 @@ } //-------------------------------------------------------------- - sl.getFile = function (filename, delay) + sl.showFile = function (filename, delay) { $('.FilterLogFilename', $frm.filter).val(filename); + sl.setFileTitle(filename); if (!sl.data.awaitingGetFileResponse) { - $pnl.msgs.hide().empty().append($('
' + sax.msg.dataQuerying + '
')).fadeIn(200); + sl.setFileData($('
' + sax.msg.dataQuerying + '
')); + sax.openDialog($dlg.file, $(win).width(), $(win).height()); } - sax.ajax.getFile = sax.ajax.getFile || { url: sax.url.base + 'System/Ajax/Logs/GetFile', formId: sl.$frm.filter.prop('id'), callbackFn: sl.doGetFiles }; + sax.ajax.getFile = sax.ajax.getFile || { url: sax.url.base + 'System/Ajax/Logs/GetFile', formId: sl.$frm.filter.prop('id'), callbackFn: sl.onShowFile }; sax.doAjaxRequest(sax.ajax.getFile, delay || 50); sl.data.awaitingGetFileResponse = true; } - sl.doGetFiles = function (response) + sl.onShowFile = function (response) { //sax.debug('doGetFiles', response); delete sl.data.awaitingGetFileResponse; $pnl.msgs.hide().empty(); if (!response.success) return; + // Configure file action buttons. + let files = sl.data.files; + let filename = response.file.name; + $ctrl.filePrev.prop('disabled', (files || []).length < 2 || !filename || files[0].name == filename); + $ctrl.fileNext.prop('disabled', (files || []).length < 2 || !filename || files[files.length - 1].name == filename); + // Display table details. - $dlg.file.dialog('option', 'title', "File: " + response.file.name || response.popupTitle); - $('.FileData', $dlg.file).html(response.file.html); + sl.setFileTitle(filename || response.popupTitle); + $('.LogFilename', $dlg.file).val(filename); + sl.setFileData(response.file.html); sax.openDialog($dlg.file, $(win).width(), $(win).height()); } + sl.setFileTitle = function (filename) + { + $dlg.file.dialog('option', 'title', "File: " + filename); + } + sl.setFileData = function (data) + { + $('.FileData', $dlg.file).html(data); + } + + //-------------------------------------------------------------- + sl.onCreateLogFileDlg = function () + { + let $btnPane = $('#dlgLogFile + div.ui-dialog-buttonpane'); + $pnl.logFileActions = $('
'); + $ctrl.filePrev = $('