diff --git a/PPMTool.Data/Entities/Person.cs b/PPMTool.Data/Entities/Person.cs index 7823693e..63d049c5 100644 --- a/PPMTool.Data/Entities/Person.cs +++ b/PPMTool.Data/Entities/Person.cs @@ -308,5 +308,23 @@ public double GetWorkloadModelTotalOnDate(DateTime date) .OrderBy(x => x.ChangeDate) .LastOrDefault(); } + + /// + /// Shadow property to store a comma-separated list of the person's skill tag names. + /// Used for filtering on the Manage People page. + /// + [NotMapped] + public string SkillTags + { + get => string.Join(", ", OwnedSkills.Select(skill => skill.SkillTag.Name).OrderBy(name => name)); + } + + /// + /// Shadow property to store the current grade of the person based on the person's current WLM and the date. + /// Used for filtering by Grade on the Manage People page currently. + /// + /// + [NotMapped] + public int? CurrentGrade { get => GetGradeOnDate(DateTime.Today); } } } diff --git a/PPMTool.Data/Entities/Project.cs b/PPMTool.Data/Entities/Project.cs index c1fbe84d..ab1a1e77 100644 --- a/PPMTool.Data/Entities/Project.cs +++ b/PPMTool.Data/Entities/Project.cs @@ -136,6 +136,14 @@ public class Project : BaseTask, ILoggableObject /// public virtual ICollection FundingSources { get; set; } = new List(); + /// + /// Shadow property to allow easy filtering on FundsReceived in datagrids. + /// Requires additional logic in the service layer to populate this property when retrieving projects from the database. + /// + /// + [NotMapped] + public double FundsReceived { get; set; } + /// /// Constructor also adds default status messages /// diff --git a/PPMTool.Data/Entities/Setting.cs b/PPMTool.Data/Entities/Setting.cs index fe2a46d4..d1ac2fbf 100644 --- a/PPMTool.Data/Entities/Setting.cs +++ b/PPMTool.Data/Entities/Setting.cs @@ -23,7 +23,8 @@ public class Setting : ILoggableObject public SettingType SettingType { get; set; } /// - /// Shadow property to get the name of the setting type as a string for use in the UI. This is not mapped to the database as it is derived from the SettingType enum. + /// Shadow property to get the name of the setting type as a string for use in the UI. + /// This is not mapped to the database as it is derived from the SettingType enum. /// [NotMapped] public string SettingTypeAsText => SettingType.ToString(); diff --git a/PPMTool.Data/Entities/TimesheetEntry.cs b/PPMTool.Data/Entities/TimesheetEntry.cs index 53903ad5..704d6928 100644 --- a/PPMTool.Data/Entities/TimesheetEntry.cs +++ b/PPMTool.Data/Entities/TimesheetEntry.cs @@ -73,19 +73,20 @@ public class TimesheetEntry : ILoggableObject public double SundayHours { get; set; } /// - /// Total hours for the week for this entry + /// Total hours for the week for this entry. This must be explicitly updated with the method. /// [NotMapped] public double TotalHours { get; private set; } /// - /// Represents whether this task is part of the user's template + /// Represents whether this task is part of the user's template. This must be explicitly set with the method. /// [NotMapped] - public bool IsInTemplate { get; set; } + public bool IsInTemplate { get; private set; } /// - /// Method to sum up the hours in the entry + /// Method to sum up the hours in the entry and update the TotalHours property. + /// This should be called whenever the hours for any day are modified. /// /// public void UpdateTotalHours() @@ -93,6 +94,15 @@ public void UpdateTotalHours() TotalHours = MondayHours + TuesdayHours + WednesdayHours + ThursdayHours + FridayHours + SaturdayHours + SundayHours; } + /// + /// Sets the IsInTemplate property to indicate whether this task is part of the user's template. + /// + /// + public void SetIsInTemplate(bool isInTemplate) + { + IsInTemplate = isInTemplate; + } + /// /// Returns a useful string to identify the entity /// diff --git a/PPMTool/Pages/AddTimesheet.razor.cs b/PPMTool/Pages/AddTimesheet.razor.cs index 6f3a8e9b..5ff63ec2 100644 --- a/PPMTool/Pages/AddTimesheet.razor.cs +++ b/PPMTool/Pages/AddTimesheet.razor.cs @@ -234,7 +234,7 @@ private List OrderByTemplate(Timesheet timesheet, string templat // Sets a boolean for use in the datagrid to show which items are part of the template foreach (TimesheetEntry e in orderedResults) { - e.IsInTemplate = order.Contains(e.InnateCodeTask.InnateCodeTaskId); + e.SetIsInTemplate(order.Contains(e.InnateCodeTask.InnateCodeTaskId)); } return orderedResults; diff --git a/PPMTool/Pages/People.razor b/PPMTool/Pages/People.razor index 52e01d8e..a048dbf0 100644 --- a/PPMTool/Pages/People.razor +++ b/PPMTool/Pages/People.razor @@ -64,7 +64,7 @@ SPDX-License-Identifier: apache-2.0 - + - - - + + + - + @if (skillsEnabled) @@ -95,7 +95,7 @@ SPDX-License-Identifier: apache-2.0 - + diff --git a/PPMTool/Pages/People.razor.cs b/PPMTool/Pages/People.razor.cs index bd54972e..d26baa98 100644 --- a/PPMTool/Pages/People.razor.cs +++ b/PPMTool/Pages/People.razor.cs @@ -112,87 +112,45 @@ private void LoadData(LoadDataArgs args) query = query.Where(x => ActiveUser.Person != null && x.PersonId == ActiveUser.Person!.PersonId); } - // Now apply the custom filters (pattern matching syntax for non-null object) - if (args.Filters is { } filters && filters.Any()) + // ---- GRID FILTERING ---- + if (!string.IsNullOrWhiteSpace(args.Filter)) { - // Filter on Skill Tags - var filter = args.Filters.FirstOrDefault(x => x.Property == "SkillTags"); - var filterValue = filter?.FilterValue as string; - if (filter != null && filterValue != null) - { - query = query.Where(x => x.OwnedSkills.Any(x => x.SkillTag.Name.Trim().ToLower().Contains(filterValue.Trim().ToLower()))); - } + query = query.Where(args.Filter); + } + + // ---- SORTING ---- + if (args.Sorts != null && args.Sorts.Count() > 0) + { + var sort = args.Sorts.First(); - // Add filter on Line Manager - filter = filters.FirstOrDefault(f => f.Property == "LineManager"); - filterValue = (filter?.FilterValue as string)?.Trim(); - if (!string.IsNullOrEmpty(filterValue)) + // Special-case sort + if (sort.Property == "SkillsCount") { - var filterValueLower = filterValue.ToLower(); - query = query.Where(x => - x.LineManager != null && - (x.LineManager.Name ?? "").Trim().ToLower().Contains(filterValueLower)); + query = sort.SortOrder == SortOrder.Ascending + ? query.OrderBy(x => x.OwnedSkills.Count()) + : query.OrderByDescending(x => x.OwnedSkills.Count()); } - - // Add any filters than are none of the special ones - var stdFilters = args.Filters.Where(x => x.Property != "SkillTags" && x.Property != "LineManager"); - if (stdFilters.Any()) + else { - // Filter via the Where method - query = query.Where(args.Filter); + query = query.OrderBy(args.OrderBy); } } - - // Sorting needed - if (!string.IsNullOrEmpty(args.OrderBy)) + else if (!string.IsNullOrWhiteSpace(args.OrderBy)) { - // Check details of the sort - if (args.Sorts is { } sorts && sorts.Any()) - { - var sort = args.Sorts?.First(); - if (sort.Property == "SkillsCount") - { - // Apply the ordering process on skills count manually - if (sort.SortOrder == SortOrder.Ascending) - { - query = query.OrderBy(x => TagService.GetCountForPerson(Context, x.PersonId)); - } - else - { - query = query.OrderByDescending(x => TagService.GetCountForPerson(Context, x.PersonId)); - } - } - else if (sort.Property == "LineManager") - { - query = sort.SortOrder == SortOrder.Ascending ? - query.OrderBy(x => x.LineManager != null ? x.LineManager.Name : "") : - query.OrderByDescending(x => x.LineManager != null ? x.LineManager.Name : ""); - } - - // No special handling required - else - { - // Sort via the OrderBy method - query = query.OrderBy(args.OrderBy); - } - } + query = query.OrderBy(args.OrderBy); } - // Important!!! Make sure the Count property of RadzenDataGrid is set. - var data = query.ToList(); - count = data.Count(); + // ---- COUNT BEFORE PAGING ---- + count = query.Count(); - // Perform paging via Skip and Take. - if (args.Skip == null) - { - people = data.Take(pageCount).ToList(); - } - else - { - people = data.Skip(args.Skip.Value).Take(args.Top.Value).ToList(); - } + // ---- PAGING ---- + var skip = args.Skip ?? 0; + var take = args.Top ?? pageCount; + + people = query.Skip(skip).Take(take).ToList(); - Debug.WriteLine($"** {data.Count()} people loaded. {people.Count()} displayed."); + Debug.WriteLine($"** {count} people loaded. {people.Count()} displayed."); } + } } \ No newline at end of file diff --git a/PPMTool/Pages/Projects.razor b/PPMTool/Pages/Projects.razor index 1ccabce3..385c2414 100644 --- a/PPMTool/Pages/Projects.razor +++ b/PPMTool/Pages/Projects.razor @@ -44,7 +44,7 @@ SPDX-License-Identifier: apache-2.0 - + @@ -71,18 +71,25 @@ SPDX-License-Identifier: apache-2.0 @p.ProjectStatus.ToNiceString() - + - + - + - + @@ -113,20 +127,48 @@ SPDX-License-Identifier: apache-2.0 @(p.CostModel.GetAttribute().Name) - + - - - + + + } - +