diff --git a/DigitalLearningSolutions.Data/Models/User/AdminUser.cs b/DigitalLearningSolutions.Data/Models/User/AdminUser.cs index 38071ad480..2fdae412d9 100644 --- a/DigitalLearningSolutions.Data/Models/User/AdminUser.cs +++ b/DigitalLearningSolutions.Data/Models/User/AdminUser.cs @@ -39,6 +39,7 @@ public class AdminUser : User public bool IsLocked => FailedLoginCount >= FailedLoginThreshold; public bool IsCmsAdministrator => ImportOnly && IsContentManager; + public bool IsCmsManager => IsContentManager && !ImportOnly; public override UserReference ToUserReference() { diff --git a/DigitalLearningSolutions.Web.Tests/Extensions/QueryableExtensionsTests.cs b/DigitalLearningSolutions.Web.Tests/Extensions/QueryableExtensionsTests.cs index 9fa1bfbac5..b0e870777c 100644 --- a/DigitalLearningSolutions.Web.Tests/Extensions/QueryableExtensionsTests.cs +++ b/DigitalLearningSolutions.Web.Tests/Extensions/QueryableExtensionsTests.cs @@ -1,5 +1,6 @@ namespace DigitalLearningSolutions.Web.Tests.Extensions { + using System.Linq; using DigitalLearningSolutions.Web.Extensions; using DigitalLearningSolutions.Web.Tests.TestHelpers; using FluentAssertions; @@ -7,15 +8,24 @@ public class QueryableExtensionsTests { + private static readonly SortableItem ItemA1 = new SortableItem("a", 1); + private static readonly SortableItem ItemA3 = new SortableItem("a", 3); + private static readonly SortableItem ItemB2 = new SortableItem("b", 2); + private static readonly SortableItem ItemB3 = new SortableItem("b", 3); + private static readonly SortableItem ItemC3 = new SortableItem("c", 3); + private static readonly IQueryable InputItems = new[] { ItemB2, ItemA1, ItemC3 }.AsQueryable(); + + private static readonly IQueryable ThenByInputItems = + new[] { ItemB2, ItemA1, ItemA3 }.AsQueryable(); + [Test] public void SortAllItems_by_name_ascending_returns_item_in_expected_order() { // Given - var inputItems = QueryableHelper.GetListOfSortableItems("b", 2, "a", 1, "c", 3); - var expectedItems = QueryableHelper.GetListOfSortableItems("a", 1, "b", 2, "c", 3); + var expectedItems = new[] { ItemA1, ItemB2, ItemC3 }.AsQueryable(); // When - var result = inputItems.OrderBy("Name"); + var result = InputItems.OrderBy("Name"); // Then result.Should().BeEquivalentTo(expectedItems); @@ -25,11 +35,10 @@ public void SortAllItems_by_name_ascending_returns_item_in_expected_order() public void SortAllItems_by_name_descending_returns_item_in_expected_order() { // Given - var inputItems = QueryableHelper.GetListOfSortableItems("b", 2, "a", 1, "c", 3); - var expectedItems = QueryableHelper.GetListOfSortableItems("c", 3, "b", 2, "a", 1); + var expectedItems = new[] { ItemC3, ItemB2, ItemA1 }.AsQueryable(); // When - var result = inputItems.OrderByDescending("Name"); + var result = InputItems.OrderByDescending("Name"); // Then result.Should().BeEquivalentTo(expectedItems); @@ -39,11 +48,10 @@ public void SortAllItems_by_name_descending_returns_item_in_expected_order() public void SortAllItems_by_number_ascending_returns_item_in_expected_order() { // Given - var inputItems = QueryableHelper.GetListOfSortableItems("b", 2, "a", 1, "c", 3); - var expectedItems = QueryableHelper.GetListOfSortableItems("a", 1, "b", 2, "c", 3); + var expectedItems = new[] { ItemA1, ItemB2, ItemC3 }.AsQueryable(); // When - var result = inputItems.OrderBy("Number"); + var result = InputItems.OrderBy("Number"); // Then result.Should().BeEquivalentTo(expectedItems); @@ -53,11 +61,10 @@ public void SortAllItems_by_number_ascending_returns_item_in_expected_order() public void SortAllItems_by_number_descending_returns_item_in_expected_order() { // Given - var inputItems = QueryableHelper.GetListOfSortableItems("b", 2, "a", 1, "c", 3); - var expectedItems = QueryableHelper.GetListOfSortableItems("c", 3, "b", 2, "a", 1); + var expectedItems = new[] { ItemC3, ItemB2, ItemA1 }.AsQueryable(); // When - var result = inputItems.OrderByDescending("Number"); + var result = InputItems.OrderByDescending("Number"); // Then result.Should().BeEquivalentTo(expectedItems); @@ -67,11 +74,10 @@ public void SortAllItems_by_number_descending_returns_item_in_expected_order() public void SortAllItems_by_name_then_number_ascending_returns_item_in_expected_order() { // Given - var inputItems = QueryableHelper.GetListOfSortableItems("b", 2, "a", 1, "a", 3); - var expectedItems = QueryableHelper.GetListOfSortableItems("a", 1, "a", 3, "b", 2); + var expectedItems = new[] { ItemA1, ItemA3, ItemB2 }.AsQueryable(); // When - var result = inputItems.OrderBy("Name").ThenBy("Number"); + var result = ThenByInputItems.OrderBy("Name").ThenBy("Number"); // Then result.Should().BeEquivalentTo(expectedItems); @@ -81,11 +87,37 @@ public void SortAllItems_by_name_then_number_ascending_returns_item_in_expected_ public void SortAllItems_by_name_then_number_descending_returns_item_in_expected_order() { // Given - var inputItems = QueryableHelper.GetListOfSortableItems("b", 2, "a", 1, "a", 3); - var expectedItems = QueryableHelper.GetListOfSortableItems("b", 2, "a", 3, "a", 1); + var expectedItems = new[] { ItemB2, ItemA3, ItemA1 }.AsQueryable(); + + // When + var result = ThenByInputItems.OrderByDescending("Name").ThenByDescending("Number"); + + // Then + result.Should().BeEquivalentTo(expectedItems); + } + + [Test] + public void Where_returns_expected_items_for_string_property() + { + // Given + var expectedItems = new[] { ItemA1, ItemA3 }.AsQueryable(); + + // When + var result = ThenByInputItems.Where("Name", "a"); + + // Then + result.Should().BeEquivalentTo(expectedItems); + } + + [Test] + public void Where_returns_expected_items_for_int_property() + { + // Given + var inputItems = new[] { ItemA3, ItemB3, ItemA1 }.AsQueryable(); + var expectedItems = new[] { ItemA3, ItemB3 }.AsQueryable(); // When - var result = inputItems.OrderByDescending("Name").ThenByDescending("Number"); + var result = inputItems.Where("Number", 3); // Then result.Should().BeEquivalentTo(expectedItems); diff --git a/DigitalLearningSolutions.Web.Tests/Helpers/FilterableTagHelperTests.cs b/DigitalLearningSolutions.Web.Tests/Helpers/FilterableTagHelperTests.cs index d0df71a49b..9a385a43ae 100644 --- a/DigitalLearningSolutions.Web.Tests/Helpers/FilterableTagHelperTests.cs +++ b/DigitalLearningSolutions.Web.Tests/Helpers/FilterableTagHelperTests.cs @@ -1,9 +1,10 @@ namespace DigitalLearningSolutions.Web.Tests.Helpers { + using System.Collections.Generic; using System.Linq; - using DigitalLearningSolutions.Data.Models.User; using DigitalLearningSolutions.Data.Tests.TestHelpers; using DigitalLearningSolutions.Web.Helpers; + using DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage; using FluentAssertions; using FluentAssertions.Execution; using NUnit.Framework; @@ -15,6 +16,15 @@ public void GetCurrentTagsForAdminUser_should_return_correct_tags() { // Given var adminUser = UserTestHelper.GetDefaultAdminUser(failedLoginCount: 5, isContentCreator: true); + var expectedTags = new List + { + new SearchableTagViewModel(AdminFilterOptions.IsLocked), + new SearchableTagViewModel(AdminFilterOptions.CentreAdministrator), + new SearchableTagViewModel(AdminFilterOptions.Supervisor), + new SearchableTagViewModel(AdminFilterOptions.Trainer), + new SearchableTagViewModel(AdminFilterOptions.CmsAdministrator), + new SearchableTagViewModel(AdminFilterOptions.ContentCreatorLicense) + }; // When var result = FilterableTagHelper.GetCurrentTagsForAdminUser(adminUser).ToList(); @@ -22,14 +32,7 @@ public void GetCurrentTagsForAdminUser_should_return_correct_tags() // Then using (new AssertionScope()) { - result.Count.Should().Be(7); - result.Should().Contain(("Locked", nameof(AdminUser.IsLocked) + "|true")); - result.Should().Contain(("Centre administrator", nameof(AdminUser.IsCentreAdmin) + "|true")); - result.Should().Contain(("Supervisor", nameof(AdminUser.IsSupervisor) + "|true")); - result.Should().Contain(("Trainer", nameof(AdminUser.IsTrainer) + "|true")); - result.Should().Contain(("CMS manager", nameof(AdminUser.IsContentManager) + "|true")); - result.Should().Contain(("Content Creator license", nameof(AdminUser.IsContentCreator) + "|true")); - result.Should().Contain(("CMS administrator", nameof(AdminUser.IsCmsAdministrator) + "|true")); + result.Should().BeEquivalentTo(expectedTags); } } } diff --git a/DigitalLearningSolutions.Web.Tests/Helpers/FilteringHelperTests.cs b/DigitalLearningSolutions.Web.Tests/Helpers/FilteringHelperTests.cs new file mode 100644 index 0000000000..4bed494d2e --- /dev/null +++ b/DigitalLearningSolutions.Web.Tests/Helpers/FilteringHelperTests.cs @@ -0,0 +1,44 @@ +namespace DigitalLearningSolutions.Web.Tests.Helpers +{ + using System.Linq; + using DigitalLearningSolutions.Web.Helpers; + using DigitalLearningSolutions.Web.Tests.TestHelpers; + using FluentAssertions; + using NUnit.Framework; + + public class FilteringHelperTests + { + private static readonly SortableItem ItemA1 = new SortableItem("a", 1); + private static readonly SortableItem ItemA3 = new SortableItem("a", 3); + private static readonly SortableItem ItemB2 = new SortableItem("b", 2); + private static readonly IQueryable InputItems = new[] { ItemA1, ItemA3, ItemB2 }.AsQueryable(); + + [Test] + public void FilterItems_returns_expected_items_with_single_filter() + { + // Given + var expectedItems = new[] { ItemA1, ItemA3 }.AsQueryable(); + var filterBy = "Name|a"; + + // When + var result = FilteringHelper.FilterItems(InputItems, filterBy); + + // Then + result.Should().BeEquivalentTo(expectedItems); + } + + [Test] + public void FilterItems_returns_expected_items_with_multiple_filters() + { + // Given + var expectedItems = new[] { ItemA1 }.AsQueryable(); + var filterBy = "Name|a\r\nNumber|1"; + + // When + var result = FilteringHelper.FilterItems(InputItems, filterBy); + + // Then + result.Should().BeEquivalentTo(expectedItems); + } + } +} diff --git a/DigitalLearningSolutions.Web.Tests/TestHelpers/QueryableHelper.cs b/DigitalLearningSolutions.Web.Tests/TestHelpers/QueryableHelper.cs deleted file mode 100644 index 101244ada6..0000000000 --- a/DigitalLearningSolutions.Web.Tests/TestHelpers/QueryableHelper.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace DigitalLearningSolutions.Web.Tests.TestHelpers -{ - using System.Linq; - - public static class QueryableHelper - { - public static IQueryable GetListOfSortableItems(string name1, int number1, string name2, int number2, string name3, int number3) - { - return new[] { new SortableItem(name1, number1), new SortableItem(name2, number2), new SortableItem(name3, number3) }.AsQueryable(); - } - } -} diff --git a/DigitalLearningSolutions.Web.Tests/ViewComponents/CurrentFiltersViewComponentTests.cs b/DigitalLearningSolutions.Web.Tests/ViewComponents/CurrentFiltersViewComponentTests.cs new file mode 100644 index 0000000000..602cf3e1e4 --- /dev/null +++ b/DigitalLearningSolutions.Web.Tests/ViewComponents/CurrentFiltersViewComponentTests.cs @@ -0,0 +1,65 @@ +namespace DigitalLearningSolutions.Web.Tests.ViewComponents +{ + using System.Collections.Generic; + using DigitalLearningSolutions.Data.Models.User; + using DigitalLearningSolutions.Web.Helpers; + using DigitalLearningSolutions.Web.ViewComponents; + using DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage; + using DigitalLearningSolutions.Web.ViewModels.TrackingSystem.Centre.Administrator; + using FluentAssertions; + using Microsoft.AspNetCore.Http; + using Microsoft.AspNetCore.Mvc.Rendering; + using Microsoft.AspNetCore.Mvc.ViewComponents; + using NUnit.Framework; + + public class CurrentFiltersViewComponentTests + { + private ViewComponentContext viewComponentContext = null!; + + [SetUp] + public void Setup() + { + var httpContext = new DefaultHttpContext(); + + var viewContext = new ViewContext { HttpContext = httpContext }; + + viewComponentContext = new ViewComponentContext + { + ViewContext = viewContext + }; + } + + [Test] + public void CurrentFiltersViewComponent_selects_expected_filters_to_display() + { + // Given + var viewComponent = new CurrentFiltersViewComponent { ViewComponentContext = viewComponentContext }; + var categories = new[] { "Word", "Excel" }; + const string searchString = "test"; + var inputViewModel = new CentreAdministratorsViewModel( + 1, + new List(), + categories, + searchString, + "SearchableName", + "Ascending", + $"CategoryName|Word\r\n{AdminFilterOptions.CentreAdministrator.FilterValue}", + 1 + ); + var expectedAppliedFilters = new List + { + new AppliedFilterViewModel(AdminFilterOptions.CentreAdministrator.DisplayText, "Role"), + new AppliedFilterViewModel("Word", "Category") + }; + + var expectedFilterViewModel = new CurrentFiltersViewModel(expectedAppliedFilters, searchString); + + // When + var model = viewComponent.Invoke(inputViewModel).As().ViewData.Model + .As(); + + // Then + model.Should().BeEquivalentTo(expectedFilterViewModel); + } + } +} diff --git a/DigitalLearningSolutions.Web.Tests/ViewModels/Common/NumberOfAdministratorViewModelTests.cs b/DigitalLearningSolutions.Web.Tests/ViewModels/Common/NumberOfAdministratorViewModelTests.cs index 12bc98c4f3..f2b9ae13f6 100644 --- a/DigitalLearningSolutions.Web.Tests/ViewModels/Common/NumberOfAdministratorViewModelTests.cs +++ b/DigitalLearningSolutions.Web.Tests/ViewModels/Common/NumberOfAdministratorViewModelTests.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using DigitalLearningSolutions.Data.Models.User; using DigitalLearningSolutions.Data.Tests.TestHelpers; - using DigitalLearningSolutions.Web.ViewModels.Common; + using DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents; using FluentAssertions; using NUnit.Framework; @@ -27,8 +27,8 @@ public void AdminUsers_and_Centre_populate_expected_values() // Then viewModel.Admins.Should().Be("7"); viewModel.Supervisors.Should().Be("6"); - viewModel.CmsAdministrators.Should().Be("4 / 12"); - viewModel.CmsManagers.Should().Be("1 / 13"); + viewModel.CmsAdministrators.Should().Be("3 / 12"); + viewModel.CmsManagers.Should().Be("2 / 13"); viewModel.CcLicences.Should().Be("2 / 14"); viewModel.Trainers.Should().Be("1 / 15"); } @@ -49,8 +49,8 @@ public void No_limit_should_be_displayed_when_centre_has_no_limit_on_spots_avail var viewModel = new NumberOfAdministratorsViewModel(centre, adminUsersAtCentre); // Then - viewModel.CmsAdministrators.Should().Be("4"); - viewModel.CmsManagers.Should().Be("1"); + viewModel.CmsAdministrators.Should().Be("3"); + viewModel.CmsManagers.Should().Be("2"); viewModel.CcLicences.Should().Be("2"); viewModel.Trainers.Should().Be("1"); } diff --git a/DigitalLearningSolutions.Web.Tests/ViewModels/TrackingSystem/Centre/Administrator/CentreAdministratorsViewModelTests.cs b/DigitalLearningSolutions.Web.Tests/ViewModels/TrackingSystem/Centre/Administrator/CentreAdministratorsViewModelTests.cs index 419be5ad85..4ae2de7b01 100644 --- a/DigitalLearningSolutions.Web.Tests/ViewModels/TrackingSystem/Centre/Administrator/CentreAdministratorsViewModelTests.cs +++ b/DigitalLearningSolutions.Web.Tests/ViewModels/TrackingSystem/Centre/Administrator/CentreAdministratorsViewModelTests.cs @@ -1,8 +1,11 @@ namespace DigitalLearningSolutions.Web.Tests.ViewModels.TrackingSystem.Centre.Administrator { + using System.Collections.Generic; using System.Linq; using DigitalLearningSolutions.Data.Models.User; using DigitalLearningSolutions.Data.Tests.TestHelpers; + using DigitalLearningSolutions.Web.Helpers; + using DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage; using DigitalLearningSolutions.Web.ViewModels.TrackingSystem.Centre.Administrator; using FluentAssertions; using NUnit.Framework; @@ -34,9 +37,11 @@ public void Centre_administrators_should_default_to_returning_the_first_ten_admi var model = new CentreAdministratorsViewModel( 1, adminUsers, + new List(), null, "SearchableName", "Ascending", + null, 1 ); @@ -50,14 +55,57 @@ public void Centre_administrators_should_correctly_return_the_second_page_of_adm var model = new CentreAdministratorsViewModel( 1, adminUsers, + new List(), null, "SearchableName", "Ascending", + null, 2 ); model.Admins.Count().Should().Be(5); model.Admins.First().Name.Should().BeEquivalentTo("k Surname"); } + + [Test] + public void Centre_Administrators_filters_should_be_set() + { + // Given + var roleOptions = new[] + { + AdminFilterOptions.CentreAdministrator, + AdminFilterOptions.Supervisor, + AdminFilterOptions.Trainer, + AdminFilterOptions.ContentCreatorLicense, + AdminFilterOptions.CmsAdministrator, + AdminFilterOptions.CmsManager + }; + var accountStatusOptions = new[] + { + AdminFilterOptions.IsLocked, + AdminFilterOptions.IsNotLocked + }; + var expectedFilters = new[] + { + new FilterViewModel("Role", "Role", roleOptions), + new FilterViewModel("CategoryName", "Category", new List()), + new FilterViewModel("AccountStatus", "Account Status", accountStatusOptions) + }.AsEnumerable(); + + // When + var model = new CentreAdministratorsViewModel( + 1, + adminUsers, + new List(), + null, + "SearchableName", + "Ascending", + null, + 2 + ); + + // Then + model.Filters.Should().BeEquivalentTo(expectedFilters); + } } } diff --git a/DigitalLearningSolutions.Web.Tests/ViewModels/TrackingSystem/Centre/ContractDetailsViewModelTests.cs b/DigitalLearningSolutions.Web.Tests/ViewModels/TrackingSystem/Centre/ContractDetailsViewModelTests.cs index f1f5efd5a0..57219d1ac8 100644 --- a/DigitalLearningSolutions.Web.Tests/ViewModels/TrackingSystem/Centre/ContractDetailsViewModelTests.cs +++ b/DigitalLearningSolutions.Web.Tests/ViewModels/TrackingSystem/Centre/ContractDetailsViewModelTests.cs @@ -30,9 +30,9 @@ public void AdminUsers_and_Centre_populate_expected_values() // Then viewModel.Administrators.Should().Be("7"); viewModel.Supervisors.Should().Be("6"); - viewModel.CmsAdministrators.Should().Be("4 / 3"); + viewModel.CmsAdministrators.Should().Be("3 / 3"); viewModel.CmsAdministratorsColour.Should().Be("red"); - viewModel.CmsManagers.Should().Be("1 / 13"); + viewModel.CmsManagers.Should().Be("2 / 13"); viewModel.CmsManagersColour.Should().Be("green"); viewModel.ContentCreators.Should().Be("2 / 14"); viewModel.ContentCreatorsColour.Should().Be("green"); @@ -65,9 +65,9 @@ public void AdminUsers_and_Centre_populate_expected_values_with_no_limit() // Then viewModel.Administrators.Should().Be("7"); viewModel.Supervisors.Should().Be("6"); - viewModel.CmsAdministrators.Should().Be("4"); + viewModel.CmsAdministrators.Should().Be("3"); viewModel.CmsAdministratorsColour.Should().Be("blue"); - viewModel.CmsManagers.Should().Be("1"); + viewModel.CmsManagers.Should().Be("2"); viewModel.CmsManagersColour.Should().Be("blue"); viewModel.ContentCreators.Should().Be("2"); viewModel.ContentCreatorsColour.Should().Be("blue"); diff --git a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Frameworks.cs b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Frameworks.cs index 5ce4144228..d125108262 100644 --- a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Frameworks.cs +++ b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Frameworks.cs @@ -14,7 +14,7 @@ namespace DigitalLearningSolutions.Web.Controllers.FrameworksController { - using DigitalLearningSolutions.Web.ViewModels.Common; + using DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage; public partial class FrameworksController { diff --git a/DigitalLearningSolutions.Web/Controllers/LearningPortalController/Available.cs b/DigitalLearningSolutions.Web/Controllers/LearningPortalController/Available.cs index 5310d9ce39..19a536ef7d 100644 --- a/DigitalLearningSolutions.Web/Controllers/LearningPortalController/Available.cs +++ b/DigitalLearningSolutions.Web/Controllers/LearningPortalController/Available.cs @@ -1,7 +1,7 @@ namespace DigitalLearningSolutions.Web.Controllers.LearningPortalController { using DigitalLearningSolutions.Web.Helpers; - using DigitalLearningSolutions.Web.ViewModels.Common; + using DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage; using DigitalLearningSolutions.Web.ViewModels.LearningPortal.Available; using Microsoft.AspNetCore.Mvc; diff --git a/DigitalLearningSolutions.Web/Controllers/LearningPortalController/Completed.cs b/DigitalLearningSolutions.Web/Controllers/LearningPortalController/Completed.cs index 4e62b52da9..3f17a7d88a 100644 --- a/DigitalLearningSolutions.Web/Controllers/LearningPortalController/Completed.cs +++ b/DigitalLearningSolutions.Web/Controllers/LearningPortalController/Completed.cs @@ -1,7 +1,7 @@ namespace DigitalLearningSolutions.Web.Controllers.LearningPortalController { using DigitalLearningSolutions.Web.Helpers; - using DigitalLearningSolutions.Web.ViewModels.Common; + using DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage; using DigitalLearningSolutions.Web.ViewModels.LearningPortal.Completed; using Microsoft.AspNetCore.Mvc; diff --git a/DigitalLearningSolutions.Web/Controllers/LearningPortalController/Current.cs b/DigitalLearningSolutions.Web/Controllers/LearningPortalController/Current.cs index 53e5f2c71b..a1dfcecfe9 100644 --- a/DigitalLearningSolutions.Web/Controllers/LearningPortalController/Current.cs +++ b/DigitalLearningSolutions.Web/Controllers/LearningPortalController/Current.cs @@ -4,7 +4,7 @@ using System.Linq; using DigitalLearningSolutions.Web.ControllerHelpers; using DigitalLearningSolutions.Web.Helpers; - using DigitalLearningSolutions.Web.ViewModels.Common; + using DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage; using DigitalLearningSolutions.Web.ViewModels.LearningPortal.Current; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; diff --git a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/Administrator/AdministratorController.cs b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/Administrator/AdministratorController.cs index 001680aba0..1b5684dc93 100644 --- a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/Administrator/AdministratorController.cs +++ b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/Administrator/AdministratorController.cs @@ -1,8 +1,10 @@ namespace DigitalLearningSolutions.Web.Controllers.TrackingSystem.Centre.Administrator { + using System.Linq; using DigitalLearningSolutions.Data.DataServices; + using DigitalLearningSolutions.Data.Services; using DigitalLearningSolutions.Web.Helpers; - using DigitalLearningSolutions.Web.ViewModels.Common; + using DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage; using DigitalLearningSolutions.Web.ViewModels.TrackingSystem.Centre.Administrator; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -11,26 +13,43 @@ [Route("TrackingSystem/Centre/Administrators")] public class AdministratorController : Controller { + private readonly ICommonService commonService; private readonly IUserDataService userDataService; - public AdministratorController(IUserDataService userDataService) + public AdministratorController( + IUserDataService userDataService, + ICommonService commonService + ) { this.userDataService = userDataService; + this.commonService = commonService; } [Route("{page=1:int}")] public IActionResult Index( string? searchString = null, - int page = 1 + int page = 1, + string? filterBy = null, + string? filterValue = null ) { - var adminUsersAtCentre = userDataService.GetAdminUsersByCentreId(User.GetCentreId()); + if (filterValue != null) + { + filterBy = NewlineSeparatedStringListHelper.AddStringToNewlineSeparatedList(filterBy, filterValue); + } + + var centreId = User.GetCentreId(); + var adminUsersAtCentre = userDataService.GetAdminUsersByCentreId(centreId); + var categories = commonService.GetCategoryListForCentre(centreId).Select(c => c.CategoryName); + var model = new CentreAdministratorsViewModel( - User.GetCentreId(), + centreId, adminUsersAtCentre, + categories, searchString, DefaultSortByOptions.Name.PropertyName, BaseSearchablePageViewModel.Ascending, + filterBy, page ); diff --git a/DigitalLearningSolutions.Web/Extensions/QueryableExtensions.cs b/DigitalLearningSolutions.Web/Extensions/QueryableExtensions.cs index 519ecdf2b7..54e8aac615 100644 --- a/DigitalLearningSolutions.Web/Extensions/QueryableExtensions.cs +++ b/DigitalLearningSolutions.Web/Extensions/QueryableExtensions.cs @@ -26,6 +26,11 @@ public static IOrderedQueryable ThenByDescending(this IOrderedQueryable return source.ThenByDescending(ToLambda(propertyName)); } + public static IQueryable Where(this IQueryable source, string propertyName, object? propertyValue) + { + return source.Where(ToEqualityLambda(propertyName, propertyValue)); + } + private static Expression> ToLambda(string propertyName) { var parameter = Expression.Parameter(typeof(T)); @@ -34,5 +39,14 @@ private static Expression> ToLambda(string propertyName) return Expression.Lambda>(propAsObject, parameter); } + + private static Expression> ToEqualityLambda(string propertyName, object? comparisonValue) + { + var parameter = Expression.Parameter(typeof(T)); + var property = Expression.Property(parameter, propertyName); + var objectEquality = Expression.Equal(property, Expression.Constant(comparisonValue)); + + return Expression.Lambda>(objectEquality, parameter); + } } } diff --git a/DigitalLearningSolutions.Web/Helpers/FilterableTagHelper.cs b/DigitalLearningSolutions.Web/Helpers/FilterableTagHelper.cs index 0af9b3ce61..1e480c5943 100644 --- a/DigitalLearningSolutions.Web/Helpers/FilterableTagHelper.cs +++ b/DigitalLearningSolutions.Web/Helpers/FilterableTagHelper.cs @@ -2,47 +2,51 @@ { using System.Collections.Generic; using DigitalLearningSolutions.Data.Models.User; + using DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage; public static class FilterableTagHelper { - public static IEnumerable<(string, string)> GetCurrentTagsForAdminUser(AdminUser adminUser) + public static IEnumerable GetCurrentTagsForAdminUser(AdminUser adminUser) { - var tags = new List<(string, string)>(); - - // TODO: Either change these when HEEDLS-532 is merged first or change them on HEEDLS-532 if HEEDLS-416 is merged first to the AdminFilterOptions + var tags = new List(); + if (adminUser.IsLocked) { - tags.Add(("Locked", nameof(AdminUser.IsLocked) + "|true")); + tags.Add(new SearchableTagViewModel(AdminFilterOptions.IsLocked)); + } + else + { + tags.Add(new SearchableTagViewModel(AdminFilterOptions.IsNotLocked, true)); } if (adminUser.IsCentreAdmin) { - tags.Add(("Centre administrator", nameof(AdminUser.IsCentreAdmin) + "|true")); + tags.Add(new SearchableTagViewModel(AdminFilterOptions.CentreAdministrator)); } if (adminUser.IsSupervisor) { - tags.Add(("Supervisor", nameof(AdminUser.IsSupervisor) + "|true")); + tags.Add(new SearchableTagViewModel(AdminFilterOptions.Supervisor)); } if (adminUser.IsTrainer) { - tags.Add(("Trainer", nameof(AdminUser.IsTrainer) + "|true")); + tags.Add(new SearchableTagViewModel(AdminFilterOptions.Trainer)); } if (adminUser.IsContentCreator) { - tags.Add(("Content Creator license", nameof(AdminUser.IsContentCreator) + "|true")); + tags.Add(new SearchableTagViewModel(AdminFilterOptions.ContentCreatorLicense)); } if (adminUser.IsCmsAdministrator) { - tags.Add(("CMS administrator", nameof(AdminUser.IsCmsAdministrator) + "|true")); + tags.Add(new SearchableTagViewModel(AdminFilterOptions.CmsAdministrator)); } - if (adminUser.IsContentManager) + if (adminUser.IsCmsManager) { - tags.Add(("CMS manager", nameof(AdminUser.IsContentManager) + "|true")); + tags.Add(new SearchableTagViewModel(AdminFilterOptions.CmsManager)); } return tags; diff --git a/DigitalLearningSolutions.Web/Helpers/FilteringHelper.cs b/DigitalLearningSolutions.Web/Helpers/FilteringHelper.cs new file mode 100644 index 0000000000..82feb95c03 --- /dev/null +++ b/DigitalLearningSolutions.Web/Helpers/FilteringHelper.cs @@ -0,0 +1,90 @@ +namespace DigitalLearningSolutions.Web.Helpers +{ + using System.Collections.Generic; + using System.ComponentModel; + using System.Linq; + using DigitalLearningSolutions.Data.Models; + using DigitalLearningSolutions.Data.Models.User; + using DigitalLearningSolutions.Web.Extensions; + using DigitalLearningSolutions.Web.Models.Enums; + using DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage; + + public static class FilteringHelper + { + public const char Separator = '|'; + + public static IEnumerable FilterItems( + IQueryable items, + string? filterBy + ) where T : BaseSearchableItem + { + var listOfFilters = NewlineSeparatedStringListHelper.SplitNewlineSeparatedList(filterBy); + + foreach (var filter in listOfFilters) + { + var splitFilter = filter.Split(Separator); + var propertyName = splitFilter[0]; + var propertyValueString = splitFilter[1]; + var propertyType = typeof(T).GetProperty(propertyName)!.PropertyType; + var propertyValue = TypeDescriptor.GetConverter(propertyType).ConvertFromString(propertyValueString); + + items = items.Where(propertyName, propertyValue); + } + + return items; + } + } + + public static class AdminFilterOptions + { + public static readonly FilterOptionViewModel CentreAdministrator = new FilterOptionViewModel( + "Centre administrator", + nameof(AdminUser.IsCentreAdmin) + FilteringHelper.Separator + "true", + FilterStatus.Default + ); + + public static readonly FilterOptionViewModel Supervisor = new FilterOptionViewModel( + "Supervisor", + nameof(AdminUser.IsSupervisor) + FilteringHelper.Separator + "true", + FilterStatus.Default + ); + + public static readonly FilterOptionViewModel Trainer = new FilterOptionViewModel( + "Trainer", + nameof(AdminUser.IsTrainer) + FilteringHelper.Separator + "true", + FilterStatus.Default + ); + + public static readonly FilterOptionViewModel ContentCreatorLicense = + new FilterOptionViewModel( + "Content Creator license", + nameof(AdminUser.IsContentCreator) + FilteringHelper.Separator + "true", + FilterStatus.Default + ); + + public static readonly FilterOptionViewModel CmsAdministrator = + new FilterOptionViewModel( + "CMS administrator", + nameof(AdminUser.IsCmsAdministrator) + FilteringHelper.Separator + "true", + FilterStatus.Default + ); + + public static readonly FilterOptionViewModel CmsManager = new FilterOptionViewModel( + "CMS manager", + nameof(AdminUser.IsCmsManager) + FilteringHelper.Separator + "true", + FilterStatus.Default + ); + + public static readonly FilterOptionViewModel IsLocked = new FilterOptionViewModel( + "Locked", + nameof(AdminUser.IsLocked) + FilteringHelper.Separator + "true", + FilterStatus.Warning + ); + + public static readonly FilterOptionViewModel IsNotLocked = new FilterOptionViewModel( + "Not locked", + nameof(AdminUser.IsLocked) + FilteringHelper.Separator + "false", + FilterStatus.Default + ); + } +} diff --git a/DigitalLearningSolutions.Web/Helpers/GenericSortingHelper.cs b/DigitalLearningSolutions.Web/Helpers/GenericSortingHelper.cs index f5273c7f35..7f357f31f0 100644 --- a/DigitalLearningSolutions.Web/Helpers/GenericSortingHelper.cs +++ b/DigitalLearningSolutions.Web/Helpers/GenericSortingHelper.cs @@ -6,7 +6,7 @@ using DigitalLearningSolutions.Data.Models.Courses; using DigitalLearningSolutions.Data.Models.Frameworks; using DigitalLearningSolutions.Web.Extensions; - using DigitalLearningSolutions.Web.ViewModels.Common; + using DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage; public static class GenericSortingHelper { diff --git a/DigitalLearningSolutions.Web/Helpers/SortingHelper.cs b/DigitalLearningSolutions.Web/Helpers/SortingHelper.cs index 0dea47d075..983670785d 100644 --- a/DigitalLearningSolutions.Web/Helpers/SortingHelper.cs +++ b/DigitalLearningSolutions.Web/Helpers/SortingHelper.cs @@ -1,48 +1,48 @@ -namespace DigitalLearningSolutions.Web.Helpers -{ - using System.Collections.Generic; - using System.Linq; - using DigitalLearningSolutions.Data.Models.RoleProfiles; - using DigitalLearningSolutions.Web.ViewModels.RoleProfiles; +namespace DigitalLearningSolutions.Web.Helpers +{ + using System.Collections.Generic; + using System.Linq; + using DigitalLearningSolutions.Data.Models.RoleProfiles; + using DigitalLearningSolutions.Web.ViewModels.RoleProfiles; /// - /// This is the older version of the SortingHelper. For future search/sort implementations we should - /// be using the generic version and implementing BaseSearchableItem + /// This is the older version of the SortingHelper. For future search/sort implementations we should + /// be using the generic version and implementing BaseSearchableItem /// on the entity to be sorted - /// - public static class SortingHelper - { - public static IEnumerable SortRoleProfileItems( - IEnumerable roleProfiles, - string sortBy, - string sortDirection - ) - { - return sortBy switch - { - RoleProfileSortByOptionTexts.RoleProfileName => sortDirection == BaseRoleProfilesPageViewModel.DescendingText - ? roleProfiles.OrderByDescending(roleProfile => roleProfile.RoleProfileName) - : roleProfiles.OrderBy(roleProfile => roleProfile.RoleProfileName), - RoleProfileSortByOptionTexts.RoleProfileOwner => sortDirection == BaseRoleProfilesPageViewModel.DescendingText - ? roleProfiles.OrderByDescending(roleProfile => roleProfile.Owner) - : roleProfiles.OrderBy(roleProfile => roleProfile.Owner), - RoleProfileSortByOptionTexts.RoleProfileCreatedDate => sortDirection == BaseRoleProfilesPageViewModel.DescendingText - ? roleProfiles.OrderByDescending(roleProfile => roleProfile.CreatedDate) - : roleProfiles.OrderBy(roleProfile => roleProfile.CreatedDate), - RoleProfileSortByOptionTexts.RoleProfilePublishStatus => sortDirection == BaseRoleProfilesPageViewModel.DescendingText - ? roleProfiles.OrderByDescending(roleProfile => roleProfile.PublishStatusID) - : roleProfiles.OrderBy(roleProfile => roleProfile.PublishStatusID), - RoleProfileSortByOptionTexts.RoleProfileBrand => sortDirection == BaseRoleProfilesPageViewModel.DescendingText - ? roleProfiles.OrderByDescending(roleProfile => roleProfile.Brand) - : roleProfiles.OrderBy(roleProfile => roleProfile.Brand), - RoleProfileSortByOptionTexts.RoleProfileNationalRoleProfile => sortDirection == BaseRoleProfilesPageViewModel.DescendingText - ? roleProfiles.OrderByDescending(roleProfile => roleProfile.NRPRole) - : roleProfiles.OrderBy(roleProfile => roleProfile.NRPRole), - RoleProfileSortByOptionTexts.RoleProfileNationalRoleGroup => sortDirection == BaseRoleProfilesPageViewModel.DescendingText - ? roleProfiles.OrderByDescending(roleProfile => roleProfile.NRPProfessionalGroup) - : roleProfiles.OrderBy(roleProfile => roleProfile.NRPProfessionalGroup), - _ => roleProfiles - }; - } - } -} + /// + public static class SortingHelper + { + public static IEnumerable SortRoleProfileItems( + IEnumerable roleProfiles, + string sortBy, + string sortDirection + ) + { + return sortBy switch + { + RoleProfileSortByOptionTexts.RoleProfileName => sortDirection == BaseRoleProfilesPageViewModel.DescendingText + ? roleProfiles.OrderByDescending(roleProfile => roleProfile.RoleProfileName) + : roleProfiles.OrderBy(roleProfile => roleProfile.RoleProfileName), + RoleProfileSortByOptionTexts.RoleProfileOwner => sortDirection == BaseRoleProfilesPageViewModel.DescendingText + ? roleProfiles.OrderByDescending(roleProfile => roleProfile.Owner) + : roleProfiles.OrderBy(roleProfile => roleProfile.Owner), + RoleProfileSortByOptionTexts.RoleProfileCreatedDate => sortDirection == BaseRoleProfilesPageViewModel.DescendingText + ? roleProfiles.OrderByDescending(roleProfile => roleProfile.CreatedDate) + : roleProfiles.OrderBy(roleProfile => roleProfile.CreatedDate), + RoleProfileSortByOptionTexts.RoleProfilePublishStatus => sortDirection == BaseRoleProfilesPageViewModel.DescendingText + ? roleProfiles.OrderByDescending(roleProfile => roleProfile.PublishStatusID) + : roleProfiles.OrderBy(roleProfile => roleProfile.PublishStatusID), + RoleProfileSortByOptionTexts.RoleProfileBrand => sortDirection == BaseRoleProfilesPageViewModel.DescendingText + ? roleProfiles.OrderByDescending(roleProfile => roleProfile.Brand) + : roleProfiles.OrderBy(roleProfile => roleProfile.Brand), + RoleProfileSortByOptionTexts.RoleProfileNationalRoleProfile => sortDirection == BaseRoleProfilesPageViewModel.DescendingText + ? roleProfiles.OrderByDescending(roleProfile => roleProfile.NRPRole) + : roleProfiles.OrderBy(roleProfile => roleProfile.NRPRole), + RoleProfileSortByOptionTexts.RoleProfileNationalRoleGroup => sortDirection == BaseRoleProfilesPageViewModel.DescendingText + ? roleProfiles.OrderByDescending(roleProfile => roleProfile.NRPProfessionalGroup) + : roleProfiles.OrderBy(roleProfile => roleProfile.NRPProfessionalGroup), + _ => roleProfiles + }; + } + } +} diff --git a/DigitalLearningSolutions.Web/Models/Enums/FilterStatus.cs b/DigitalLearningSolutions.Web/Models/Enums/FilterStatus.cs new file mode 100644 index 0000000000..8788315982 --- /dev/null +++ b/DigitalLearningSolutions.Web/Models/Enums/FilterStatus.cs @@ -0,0 +1,9 @@ +namespace DigitalLearningSolutions.Web.Models.Enums +{ + public enum FilterStatus + { + Default, + Warning, + Success + } +} diff --git a/DigitalLearningSolutions.Web/Styles/learningPortal/courses.scss b/DigitalLearningSolutions.Web/Styles/learningPortal/courses.scss index 76e04b9147..2c036334a7 100644 --- a/DigitalLearningSolutions.Web/Styles/learningPortal/courses.scss +++ b/DigitalLearningSolutions.Web/Styles/learningPortal/courses.scss @@ -1,4 +1,5 @@ @import "~nhsuk-frontend/packages/core/all"; +@import "../shared/searchableElements/searchableElements"; .searchable-element { background-color: #fff; diff --git a/DigitalLearningSolutions.Web/Styles/shared/searchableElements.scss b/DigitalLearningSolutions.Web/Styles/shared/searchableElements.scss deleted file mode 100644 index 15cbac6250..0000000000 --- a/DigitalLearningSolutions.Web/Styles/shared/searchableElements.scss +++ /dev/null @@ -1,337 +0,0 @@ -@import "~nhsuk-frontend/packages/core/all"; - -// Some of the NHS styles break down with the 3/4 column layout below this -// size. Since the NHS large-desktop is 990px, defined this xl-desktop size. -$xl-desktop: 1024px; - -$nhs-dark-grey: #425563; - -.tags { - display: flex; - margin-bottom: 16px; - flex-wrap: wrap; -} - -.tag { - background-color: $nhs-dark-grey; - color: white; - padding: 4px; - border-radius: 4px; - margin-right: 4px; - margin-bottom: 4px; -} - -.search-box { - border: 2px solid $nhsuk-form-border-color; - border-radius: 4px 0 0 4px; - border-right: 0; - height: 52px; - padding: 0 12px; - font-size: 16px; - width: 80%; - float: left; - margin-bottom: nhsuk-spacing(4); - -webkit-appearance: none; - outline: none; - - &:focus { - border: 4px solid $color_nhsuk_black; - box-shadow: 0 0 0 4px $nhsuk-focus-color; - margin-right: 4px; - } - - &::-webkit-search-cancel-button { - -webkit-appearance: none; - display: none; - } - - @include mq($from: tablet) { - height: 40px; - width: 70%; - } - - @include mq($from: desktop) { - width: 80%; - } - - @include mq($from: large-desktop) { - width: 85%; - } - - .js-enabled & { - border-right: 2px solid $nhsuk-form-border-color; - border-radius: 4px; - - &:focus { - border-right: 4px solid $color_nhsuk-black; - } - - @include mq($until: tablet) { - width: 100%; - } - } -} - -.nhsuk-search__submit { - background-color: #003d78; - float: left; - - .nhsuk-icon__search { - fill: #fff; - } - - &:hover { - background-color: #4c6272; - } -} - -div#search { - text-align: left; - padding: 0 16px; - - @include mq($from: desktop) { - flex-basis: 50%; - } - // IE11 hack, since otherwise this overflows horribly when setting flex-basis - @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { - flex-basis: auto; - - @include mq($from: large-desktop) { - flex-basis: 50%; - } - } -} - -.sort-row { - display: flex; - flex-direction: row; - - @include mq($until: desktop) { - margin-top: 36px; - margin-left: 16px; - margin-right: 16px; - flex-direction: column; - - .js-enabled & { - flex-direction: row; - } - } - - @include mq($until: 339px) { - flex-direction: column !important; - } -} - -.sort-select-container { - padding-right: 8px; - padding-top: 8px; - - @include mq($from: desktop) { - padding-left: 8px; - margin-top: -8px; - } -} - -.sort-row::before { - content: "Sort by:"; - position: relative; - top: -32px; - height: 0; - width: 0; - float: left; - font-weight: 600; - font-size: 19px; - color: rgb(33, 43, 50); - font-family: "Frutiger W01", Arial, sans-serif; - white-space: nowrap; - - @include mq($from: desktop) { - left: 8px; - } -} - -.sort-row .nhsuk-button { - margin-top: 0; - margin-bottom: 0; -} - -.sort-button-container { - padding-top: 8px; - - @include mq($from: desktop) { - padding-top: 0; - padding-left: 8px; - } -} - -.nhsuk-select { - border-radius: 4px; -} - -.sort-and-search { - margin-top: 32px; - display: flex; - flex-direction: column; - - @include mq($from: desktop) { - flex-direction: row; - } -} - -.label-reduced-margin { - margin-bottom: 8px; -} - -.button-small { - padding: 2px 10px -} - -.nhsuk-pagination__list { - display: flex; -} - -.pagination-button { - &--next { - float: right; - align-items: flex-start; - } - - &--previous { - float: left; - align-items: flex-end; - } -} - -.pagination-button-container { - width: 38%; -} - -.page-indicator-container { - width: 24%; - text-align: center; -} - -#page-indicator { - @include nhsuk-typography-responsive(24); - padding-top: 1px; -} - -button.nhsuk-pagination__link { - border: none; - background-color: transparent; - color: $nhsuk-link-color; - display: flex; - width: auto; - // Since IE is bad at drawing boxes the correct size - overflow: visible; - - @include mq($until: tablet) { - flex-direction: column; - } - - &.nhsuk-pagination__link--prev { - @include mq($until: tablet) { - flex-direction: column-reverse; - } - } - - &.nhsuk-pagination__link--next { - float: right; - } - - &:hover { - color: $nhsuk-link-hover-color; - } - - &:active { - color: $nhsuk-link-active-color; - } -} - -.nhsuk-pagination__link .nhsuk-icon.nhsuk-icon__arrow-right, .nhsuk-pagination__link .nhsuk-icon.nhsuk-icon__arrow-left { - display: inline-block; - position: static; - flex-shrink: 0; -} - -.nhsuk-pagination__title { - @include mq($until: tablet) { - padding: 0 !important; - } - - .nhsuk-pagination-item--next & { - text-align: left; - } -} - -.nhsuk-pagination { - padding: 0; -} - -@include mq($from: 768px, $until: $xl-desktop) { - .asset-card-header { - min-height: 222px; - } -} - -@include mq($from: $xl-desktop) { - .asset-card-header { - min-height: 163px; - } -} - -.tag.tag-article { - background-color: $nhsuk-link-active-color; -} - -.tag.tag-online.course { - background-color: $nhsuk-link-color; -} - -.tag.tag-learning.guide { - background-color: $nhsuk-link-hover-color; -} - -.tag.tag-video { - background-color: $nhsuk-link-visited-color; -} - -.playlist-title { - min-height: 72px; -} - -.asset-title-header { - padding-bottom: 25px; -} - -.asset-title-header .img-circle { - height: 47px; - width: 47px; - border-radius: 5%; - border: 1px solid #768692; - text-align: center; - display: inline-block; - margin-right: 15px; - vertical-align: middle; -} - -.asset-title-header .title { - font-size: 1.6rem; - font-weight: bold; - color: $nhsuk-secondary-text-color; - padding: 1rem 0 0 0; - display: inline-block; -} - -.img-screenshot { - max-width: 100%; - width: auto; - height: auto; - border-radius: 2%; - border: 1px solid #768692; - margin-bottom: 15px; -} - -.card-filter-tag { - margin: 0 4px 4px 0; -} diff --git a/DigitalLearningSolutions.Web/Styles/shared/searchableElements/common.scss b/DigitalLearningSolutions.Web/Styles/shared/searchableElements/common.scss new file mode 100644 index 0000000000..a6b40dd1fc --- /dev/null +++ b/DigitalLearningSolutions.Web/Styles/shared/searchableElements/common.scss @@ -0,0 +1,68 @@ +@import "~nhsuk-frontend/packages/core/all"; + +// Some of the NHS styles break down with the 3/4 column layout below this +// size. Since the NHS large-desktop is 990px, defined this xl-desktop size. +$xl-desktop: 1024px; + +$nhs-dark-grey: #425563; +$button-dark-blue: #003d78; +$button-hover-colour: #4c6272; + +.input-with-no-js-submit-button { + border: 2px solid $nhsuk-form-border-color; + border-right: 0; + border-radius: 4px 0 0 4px; + height: 52px; + padding: 0 12px; + font-size: 16px; + float: left; + margin-bottom: nhsuk-spacing(4); + outline: none; + + &:focus { + border: 4px solid $color_nhsuk_black; + box-shadow: 0 0 0 4px $nhsuk-focus-color; + margin-right: 4px; + } + + &::-webkit-search-cancel-button { + -webkit-appearance: none; + display: none; + } + + @include mq($from: tablet) { + height: 40px; + width: 70%; + } + + @include mq($from: desktop) { + width: 80%; + } + + .js-enabled & { + border-right: 2px solid $nhsuk-form-border-color; + border-radius: 4px; + + &:focus { + border-right: 4px solid $color_nhsuk-black; + } + + @include mq($until: tablet) { + width: 100%; + } + } +} + +.sort-and-search { + margin-top: 32px; + display: flex; + flex-direction: column; + + @include mq($from: desktop) { + flex-direction: row; + } +} + +.button-small { + padding: 2px 10px; +} diff --git a/DigitalLearningSolutions.Web/Styles/shared/searchableElements/filter.scss b/DigitalLearningSolutions.Web/Styles/shared/searchableElements/filter.scss new file mode 100644 index 0000000000..851d090746 --- /dev/null +++ b/DigitalLearningSolutions.Web/Styles/shared/searchableElements/filter.scss @@ -0,0 +1,62 @@ +@import "~nhsuk-frontend/packages/core/all"; +@import "common"; + +.filter-dropdown { + @extend .input-with-no-js-submit-button; + + @include mq($until: tablet) { + width: 63%; + } +} + +.filter-submit { + float: left; + width: 18%; + color: $color_nhsuk-white; + min-width: 100px; + padding: 8px 4px; + + &:hover { + background-color: $button-hover-colour; + } + + &__dropdown { + @extend .filter-submit; + background-color: $button-dark-blue; + } + + &__clear { + @extend .filter-submit; + border-radius: 4px; + background-color: $color_nhsuk-red; + } +} + +.applied-filter-container { + height: auto; + width: 100%; + float: left; + + @include mq($from: tablet) { + width: 70%; + } + + @include mq($from: desktop) { + width: 80%; + } +} + +.clear-filter-button-container { + width: 18%; + float: left; +} + +.filter-tag { + padding-top: 11px; + padding-bottom: 11px; + margin-bottom: 4px; +} + +.card-filter-tag { + margin: 0 4px 4px 0; +} diff --git a/DigitalLearningSolutions.Web/Styles/shared/searchableElements/pagination.scss b/DigitalLearningSolutions.Web/Styles/shared/searchableElements/pagination.scss new file mode 100644 index 0000000000..93e292fa54 --- /dev/null +++ b/DigitalLearningSolutions.Web/Styles/shared/searchableElements/pagination.scss @@ -0,0 +1,84 @@ +@import "~nhsuk-frontend/packages/core/all"; +@import "common"; + +.nhsuk-pagination { + padding: 0; +} + +.nhsuk-pagination__list { + display: flex; +} + +.pagination-button { + &--next { + float: right; + align-items: flex-start; + } + + &--previous { + float: left; + align-items: flex-end; + } +} + +.pagination-button-container { + width: 38%; +} + +.page-indicator-container { + width: 24%; + text-align: center; +} + +#page-indicator { + @include nhsuk-typography-responsive(24); + padding-top: 1px; +} + +button.nhsuk-pagination__link { + border: none; + background-color: transparent; + color: $nhsuk-link-color; + display: flex; + width: auto; + // Since IE is bad at drawing boxes the correct size + overflow: visible; + + @include mq($until: tablet) { + flex-direction: column; + } + + &.nhsuk-pagination__link--prev { + @include mq($until: tablet) { + flex-direction: column-reverse; + } + } + + &.nhsuk-pagination__link--next { + float: right; + } + + &:hover { + color: $nhsuk-link-hover-color; + } + + &:active { + color: $nhsuk-link-active-color; + } +} + +.nhsuk-pagination__link .nhsuk-icon.nhsuk-icon__arrow-right, .nhsuk-pagination__link .nhsuk-icon.nhsuk-icon__arrow-left { + display: inline-block; + position: static; + flex-shrink: 0; +} + +.nhsuk-pagination__title { + @include mq($until: tablet) { + padding: 0 !important; + } + + .nhsuk-pagination-item--next & { + text-align: left; + } +} diff --git a/DigitalLearningSolutions.Web/Styles/shared/searchableElements/search.scss b/DigitalLearningSolutions.Web/Styles/shared/searchableElements/search.scss new file mode 100644 index 0000000000..38b3d661ae --- /dev/null +++ b/DigitalLearningSolutions.Web/Styles/shared/searchableElements/search.scss @@ -0,0 +1,45 @@ +@import "~nhsuk-frontend/packages/core/all"; +@import "common"; + +.search-box { + @extend .input-with-no-js-submit-button; + -webkit-appearance: none; + + @include mq($until: tablet) { + width: 79%; + } + + @include mq($from: large-desktop) { + width: 85%; + } +} + +.nhsuk-search__submit { + background-color: $button-dark-blue; + float: left; + + .nhsuk-icon__search { + fill: #fff; + } + + &:hover { + background-color: $button-hover-colour; + } +} + +div#search { + text-align: left; + padding: 0 16px; + + @include mq($from: desktop) { + flex-basis: 50%; + } + // IE11 hack, since otherwise this overflows horribly when setting flex-basis + @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { + flex-basis: auto; + + @include mq($from: large-desktop) { + flex-basis: 50%; + } + } +} diff --git a/DigitalLearningSolutions.Web/Styles/shared/searchableElements/searchableElements.scss b/DigitalLearningSolutions.Web/Styles/shared/searchableElements/searchableElements.scss new file mode 100644 index 0000000000..30fa389d2a --- /dev/null +++ b/DigitalLearningSolutions.Web/Styles/shared/searchableElements/searchableElements.scss @@ -0,0 +1,6 @@ +@import "~nhsuk-frontend/packages/core/all"; +@import "search"; +@import "sort"; +@import "filter"; +@import "pagination"; +@import "tags"; diff --git a/DigitalLearningSolutions.Web/Styles/shared/searchableElements/sort.scss b/DigitalLearningSolutions.Web/Styles/shared/searchableElements/sort.scss new file mode 100644 index 0000000000..b426d1312b --- /dev/null +++ b/DigitalLearningSolutions.Web/Styles/shared/searchableElements/sort.scss @@ -0,0 +1,68 @@ +@import "~nhsuk-frontend/packages/core/all"; +@import "common"; + +.sort-dropdown { + border-radius: 4px; +} + +.sort-row { + display: flex; + flex-direction: row; + + @include mq($until: desktop) { + margin-top: 36px; + margin-left: 16px; + margin-right: 16px; + flex-direction: column; + + .js-enabled & { + flex-direction: row; + } + } + + @include mq($until: 339px) { + flex-direction: column !important; + } +} + +.sort-select-container { + padding-right: 8px; + padding-top: 8px; + + @include mq($from: desktop) { + padding-left: 8px; + margin-top: -8px; + } +} + +.sort-row::before { + content: "Sort by:"; + position: relative; + top: -32px; + height: 0; + width: 0; + float: left; + font-weight: 600; + font-size: 19px; + color: rgb(33, 43, 50); + font-family: "Frutiger W01", Arial, sans-serif; + white-space: nowrap; + + @include mq($from: desktop) { + left: 8px; + } +} + +.sort-row .nhsuk-button { + margin-top: 0; + margin-bottom: 0; +} + +.sort-button-container { + padding-top: 8px; + + @include mq($from: desktop) { + padding-top: 0; + padding-left: 8px; + } +} diff --git a/DigitalLearningSolutions.Web/Styles/shared/searchableElements/tags.scss b/DigitalLearningSolutions.Web/Styles/shared/searchableElements/tags.scss new file mode 100644 index 0000000000..49e0e1493a --- /dev/null +++ b/DigitalLearningSolutions.Web/Styles/shared/searchableElements/tags.scss @@ -0,0 +1,33 @@ +@import "~nhsuk-frontend/packages/core/all"; +@import "common"; + +.tags { + display: flex; + margin-bottom: 16px; + flex-wrap: wrap; +} + +.tag { + background-color: $nhs-dark-grey; + color: white; + padding: 4px; + border-radius: 4px; + margin-right: 4px; + margin-bottom: 4px; +} + +.tag.tag-article { + background-color: $nhsuk-link-active-color; +} + +.tag.tag-online.course { + background-color: $nhsuk-link-color; +} + +.tag.tag-learning.guide { + background-color: $nhsuk-link-hover-color; +} + +.tag.tag-video { + background-color: $nhsuk-link-visited-color; +} diff --git a/DigitalLearningSolutions.Web/Styles/trackingSystem/centreAdministrators.scss b/DigitalLearningSolutions.Web/Styles/trackingSystem/centreAdministrators.scss index fefef5b164..6b1f7a744c 100644 --- a/DigitalLearningSolutions.Web/Styles/trackingSystem/centreAdministrators.scss +++ b/DigitalLearningSolutions.Web/Styles/trackingSystem/centreAdministrators.scss @@ -1,5 +1,5 @@ @import "~nhsuk-frontend/packages/core/all"; -@import "../shared/searchableElements"; +@import "../shared/searchableElements/searchableElements"; button.nhsuk-pagination__link { &.nhsuk-pagination__link--next { diff --git a/DigitalLearningSolutions.Web/ViewComponents/ActionLinkViewComponent.cs b/DigitalLearningSolutions.Web/ViewComponents/ActionLinkViewComponent.cs index b7f1e9098d..9a7217ab2c 100644 --- a/DigitalLearningSolutions.Web/ViewComponents/ActionLinkViewComponent.cs +++ b/DigitalLearningSolutions.Web/ViewComponents/ActionLinkViewComponent.cs @@ -1,7 +1,7 @@ namespace DigitalLearningSolutions.Web.ViewComponents { using System.Collections.Generic; - using DigitalLearningSolutions.Web.ViewModels.Common; + using DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents; using Microsoft.AspNetCore.Mvc; public class ActionLinkViewComponent : ViewComponent diff --git a/DigitalLearningSolutions.Web/ViewComponents/BackLinkViewComponent.cs b/DigitalLearningSolutions.Web/ViewComponents/BackLinkViewComponent.cs index 64876feb70..0a21719137 100644 --- a/DigitalLearningSolutions.Web/ViewComponents/BackLinkViewComponent.cs +++ b/DigitalLearningSolutions.Web/ViewComponents/BackLinkViewComponent.cs @@ -1,7 +1,7 @@ namespace DigitalLearningSolutions.Web.ViewComponents { using System.Collections.Generic; - using DigitalLearningSolutions.Web.ViewModels.Common; + using DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents; using Microsoft.AspNetCore.Mvc; public class BackLinkViewComponent : ViewComponent diff --git a/DigitalLearningSolutions.Web/ViewComponents/CentreContactInfoViewComponent.cs b/DigitalLearningSolutions.Web/ViewComponents/CentreContactInfoViewComponent.cs index f319a64c47..37092ccd7f 100644 --- a/DigitalLearningSolutions.Web/ViewComponents/CentreContactInfoViewComponent.cs +++ b/DigitalLearningSolutions.Web/ViewComponents/CentreContactInfoViewComponent.cs @@ -1,7 +1,7 @@ namespace DigitalLearningSolutions.Web.ViewComponents { using DigitalLearningSolutions.Data.DataServices; - using DigitalLearningSolutions.Web.ViewModels.Common; + using DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents; using Microsoft.AspNetCore.Mvc; public class CentreContactInfoViewComponent: ViewComponent diff --git a/DigitalLearningSolutions.Web/ViewComponents/CurrentFiltersViewComponent.cs b/DigitalLearningSolutions.Web/ViewComponents/CurrentFiltersViewComponent.cs new file mode 100644 index 0000000000..5ad52ed02e --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewComponents/CurrentFiltersViewComponent.cs @@ -0,0 +1,56 @@ +namespace DigitalLearningSolutions.Web.ViewComponents +{ + using System.Collections.Generic; + using System.Linq; + using DigitalLearningSolutions.Web.Helpers; + using DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage; + using Microsoft.AspNetCore.Mvc; + + public class CurrentFiltersViewComponent : ViewComponent + { + public IViewComponentResult Invoke( + BaseSearchablePageViewModel searchablePageViewModel + ) + { + var currentFilters = + NewlineSeparatedStringListHelper.SplitNewlineSeparatedList(searchablePageViewModel.FilterString); + + var appliedFilters = currentFilters.Select( + currentFilter => PopulateAppliedFilterViewModel(searchablePageViewModel, currentFilter) + ); + + var model = new CurrentFiltersViewModel(appliedFilters, searchablePageViewModel.SearchString); + + return View(model); + } + + private static AppliedFilterViewModel PopulateAppliedFilterViewModel( + BaseSearchablePageViewModel searchablePageViewModel, + string currentFilter + ) + { + var appliedFilter = searchablePageViewModel.Filters.Single(filter => FilterOptionsContainsFilter(currentFilter, filter.FilterOptions)); + + return new AppliedFilterViewModel( + GetFilterDisplayText(currentFilter, appliedFilter.FilterOptions), + appliedFilter.FilterName + ); + } + + private static string GetFilterDisplayText( + string currentFilter, + IEnumerable filterOptions + ) + { + return filterOptions.Single(filterOption => filterOption.FilterValue == currentFilter).DisplayText; + } + + private static bool FilterOptionsContainsFilter( + string currentFilter, + IEnumerable filterOptions + ) + { + return filterOptions.Any(filterOption => filterOption.FilterValue == currentFilter); + } + } +} diff --git a/DigitalLearningSolutions.Web/ViewComponents/ErrorSummaryViewComponent.cs b/DigitalLearningSolutions.Web/ViewComponents/ErrorSummaryViewComponent.cs index e5b71a11f2..9164982a7b 100644 --- a/DigitalLearningSolutions.Web/ViewComponents/ErrorSummaryViewComponent.cs +++ b/DigitalLearningSolutions.Web/ViewComponents/ErrorSummaryViewComponent.cs @@ -2,7 +2,7 @@ { using System.Collections.Generic; using System.Linq; - using DigitalLearningSolutions.Web.ViewModels.Common; + using DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents; using Microsoft.AspNetCore.Mvc; public class ErrorSummaryViewComponent : ViewComponent diff --git a/DigitalLearningSolutions.Web/ViewComponents/FileInputViewComponent.cs b/DigitalLearningSolutions.Web/ViewComponents/FileInputViewComponent.cs index 67a0e34a1e..9d2de840c9 100644 --- a/DigitalLearningSolutions.Web/ViewComponents/FileInputViewComponent.cs +++ b/DigitalLearningSolutions.Web/ViewComponents/FileInputViewComponent.cs @@ -1,6 +1,6 @@ namespace DigitalLearningSolutions.Web.ViewComponents { - using DigitalLearningSolutions.Web.ViewModels.Common; + using DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents; using Microsoft.AspNetCore.Mvc; public class FileInputViewComponent : ViewComponent diff --git a/DigitalLearningSolutions.Web/ViewComponents/LogoViewComponent.cs b/DigitalLearningSolutions.Web/ViewComponents/LogoViewComponent.cs index df001f1783..c738986805 100644 --- a/DigitalLearningSolutions.Web/ViewComponents/LogoViewComponent.cs +++ b/DigitalLearningSolutions.Web/ViewComponents/LogoViewComponent.cs @@ -4,7 +4,7 @@ namespace DigitalLearningSolutions.Web.ViewComponents using DigitalLearningSolutions.Data.Services; using DigitalLearningSolutions.Web.Helpers; using Microsoft.AspNetCore.Mvc; - using DigitalLearningSolutions.Web.ViewModels.Common; + using DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents; public class LogoViewComponent : ViewComponent { diff --git a/DigitalLearningSolutions.Web/ViewComponents/NumberOfAdministratorsViewComponent.cs b/DigitalLearningSolutions.Web/ViewComponents/NumberOfAdministratorsViewComponent.cs index daa52d20cc..e03de2546d 100644 --- a/DigitalLearningSolutions.Web/ViewComponents/NumberOfAdministratorsViewComponent.cs +++ b/DigitalLearningSolutions.Web/ViewComponents/NumberOfAdministratorsViewComponent.cs @@ -1,7 +1,7 @@ namespace DigitalLearningSolutions.Web.ViewComponents { using DigitalLearningSolutions.Data.DataServices; - using DigitalLearningSolutions.Web.ViewModels.Common; + using DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents; using Microsoft.AspNetCore.Mvc; public class NumberOfAdministratorsViewComponent : ViewComponent diff --git a/DigitalLearningSolutions.Web/ViewComponents/SearchBoxViewComponent.cs b/DigitalLearningSolutions.Web/ViewComponents/SearchBoxViewComponent.cs index 8f1d208d49..250a17943c 100644 --- a/DigitalLearningSolutions.Web/ViewComponents/SearchBoxViewComponent.cs +++ b/DigitalLearningSolutions.Web/ViewComponents/SearchBoxViewComponent.cs @@ -1,6 +1,7 @@ namespace DigitalLearningSolutions.Web.ViewComponents { - using DigitalLearningSolutions.Web.ViewModels.Common; + using DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage; + using DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents; using Microsoft.AspNetCore.Mvc; public class SearchBoxViewComponent : ViewComponent @@ -9,10 +10,11 @@ public IViewComponentResult Invoke( string aspController, string aspAction, BaseSearchablePageViewModel searchablePageViewModel, - string label + string label, + string? cssClass ) { - return View(new SearchBoxViewModel(aspController, aspAction, searchablePageViewModel, label)); + return View(new SearchBoxViewModel(aspController, aspAction, searchablePageViewModel, label, cssClass)); } } } diff --git a/DigitalLearningSolutions.Web/ViewComponents/SelectListViewComponent.cs b/DigitalLearningSolutions.Web/ViewComponents/SelectListViewComponent.cs index 9f33c376b8..bcc009f8e3 100644 --- a/DigitalLearningSolutions.Web/ViewComponents/SelectListViewComponent.cs +++ b/DigitalLearningSolutions.Web/ViewComponents/SelectListViewComponent.cs @@ -1,7 +1,7 @@ namespace DigitalLearningSolutions.Web.ViewComponents { using System.Collections.Generic; - using DigitalLearningSolutions.Web.ViewModels.Common; + using DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Rendering; diff --git a/DigitalLearningSolutions.Web/ViewComponents/SideMenuLinkViewComponent.cs b/DigitalLearningSolutions.Web/ViewComponents/SideMenuLinkViewComponent.cs index 55aa038689..b8d9b9fdd9 100644 --- a/DigitalLearningSolutions.Web/ViewComponents/SideMenuLinkViewComponent.cs +++ b/DigitalLearningSolutions.Web/ViewComponents/SideMenuLinkViewComponent.cs @@ -1,7 +1,7 @@ namespace DigitalLearningSolutions.Web.ViewComponents { using System.Collections.Generic; - using DigitalLearningSolutions.Web.ViewModels.Common; + using DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents; using Microsoft.AspNetCore.Mvc; public class SideMenuLinkViewComponent : ViewComponent diff --git a/DigitalLearningSolutions.Web/ViewComponents/SideMenuLinkWithHrefViewComponent.cs b/DigitalLearningSolutions.Web/ViewComponents/SideMenuLinkWithHrefViewComponent.cs index bd8aec5487..08acd8f926 100644 --- a/DigitalLearningSolutions.Web/ViewComponents/SideMenuLinkWithHrefViewComponent.cs +++ b/DigitalLearningSolutions.Web/ViewComponents/SideMenuLinkWithHrefViewComponent.cs @@ -1,7 +1,7 @@ namespace DigitalLearningSolutions.Web.ViewComponents { using System.Collections.Generic; - using DigitalLearningSolutions.Web.ViewModels.Common; + using DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents; using Microsoft.AspNetCore.Mvc; public class SideMenuLinkWithHrefViewComponent : ViewComponent diff --git a/DigitalLearningSolutions.Web/ViewComponents/TermsConditionsViewComponent.cs b/DigitalLearningSolutions.Web/ViewComponents/TermsConditionsViewComponent.cs index becfc97292..8706f8384b 100644 --- a/DigitalLearningSolutions.Web/ViewComponents/TermsConditionsViewComponent.cs +++ b/DigitalLearningSolutions.Web/ViewComponents/TermsConditionsViewComponent.cs @@ -1,6 +1,6 @@ namespace DigitalLearningSolutions.Web.ViewComponents { - using DigitalLearningSolutions.Web.ViewModels.Common; + using DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents; using Microsoft.AspNetCore.Mvc; public class TermsConditionsViewComponent : ViewComponent diff --git a/DigitalLearningSolutions.Web/ViewComponents/TextAreaViewComponent.cs b/DigitalLearningSolutions.Web/ViewComponents/TextAreaViewComponent.cs index b78839b87b..0ba9600c87 100644 --- a/DigitalLearningSolutions.Web/ViewComponents/TextAreaViewComponent.cs +++ b/DigitalLearningSolutions.Web/ViewComponents/TextAreaViewComponent.cs @@ -1,7 +1,7 @@ namespace DigitalLearningSolutions.Web.ViewComponents { using System.Linq; - using DigitalLearningSolutions.Web.ViewModels.Common; + using DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents; using Microsoft.AspNetCore.Mvc; public class TextAreaViewComponent : ViewComponent diff --git a/DigitalLearningSolutions.Web/ViewComponents/TextInputViewComponent.cs b/DigitalLearningSolutions.Web/ViewComponents/TextInputViewComponent.cs index 6b60335dcd..bab186edd0 100644 --- a/DigitalLearningSolutions.Web/ViewComponents/TextInputViewComponent.cs +++ b/DigitalLearningSolutions.Web/ViewComponents/TextInputViewComponent.cs @@ -1,7 +1,7 @@ namespace DigitalLearningSolutions.Web.ViewComponents { using System.Linq; - using DigitalLearningSolutions.Web.ViewModels.Common; + using DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents; using Microsoft.AspNetCore.Mvc; public class TextInputViewComponent : ViewComponent diff --git a/DigitalLearningSolutions.Web/ViewModels/Common/ActionLinkViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Common/ActionLinkViewModel.cs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/DigitalLearningSolutions.Web/ViewModels/Common/SearchablePage/AppliedFilterViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Common/SearchablePage/AppliedFilterViewModel.cs new file mode 100644 index 0000000000..4c5747b181 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/Common/SearchablePage/AppliedFilterViewModel.cs @@ -0,0 +1,15 @@ +namespace DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage +{ + public class AppliedFilterViewModel + { + public AppliedFilterViewModel(string displayText, string filterCategory) + { + DisplayText = displayText; + FilterCategory = filterCategory; + } + + public string DisplayText { get; set; } + + public string FilterCategory { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/Common/SearchablePage/BaseFilterableViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Common/SearchablePage/BaseFilterableViewModel.cs new file mode 100644 index 0000000000..504074ae62 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/Common/SearchablePage/BaseFilterableViewModel.cs @@ -0,0 +1,14 @@ +namespace DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage +{ + using System.Collections.Generic; + + public class BaseFilterableViewModel + { + public BaseFilterableViewModel() + { + Tags = new List(); + } + + public IEnumerable Tags { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/Common/BaseSearchablePageViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Common/SearchablePage/BaseSearchablePageViewModel.cs similarity index 79% rename from DigitalLearningSolutions.Web/ViewModels/Common/BaseSearchablePageViewModel.cs rename to DigitalLearningSolutions.Web/ViewModels/Common/SearchablePage/BaseSearchablePageViewModel.cs index f92e3cdf3a..846614c52f 100644 --- a/DigitalLearningSolutions.Web/ViewModels/Common/BaseSearchablePageViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/Common/SearchablePage/BaseSearchablePageViewModel.cs @@ -1,10 +1,9 @@ -namespace DigitalLearningSolutions.Web.ViewModels.Common +namespace DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage { using System; using System.Collections.Generic; using System.Linq; using DigitalLearningSolutions.Web.Helpers; - using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Rendering; public abstract class BaseSearchablePageViewModel @@ -12,6 +11,8 @@ public abstract class BaseSearchablePageViewModel public const string Descending = "Descending"; public const string Ascending = "Ascending"; + public readonly string? FilterString; + private readonly int itemsPerPage; public readonly string? SearchString; @@ -23,28 +24,34 @@ protected BaseSearchablePageViewModel( string sortBy, string sortDirection, int page, + string? filterString = null, int itemsPerPage = 10 ) { SortBy = sortBy; SortDirection = sortDirection; SearchString = searchString; + FilterString = filterString; Page = page; + Filters = new List(); this.itemsPerPage = itemsPerPage; } - + public string SortDirection { get; set; } - + public string SortBy { get; set; } - + public int Page { get; protected set; } public int TotalPages { get; protected set; } - public IEnumerable SortBySelectListItems => SelectListHelper.MapOptionsToSelectListItems(SortOptions); + public IEnumerable SortBySelectListItems => + SelectListHelper.MapOptionsToSelectListItems(SortOptions); public abstract IEnumerable<(string, string)> SortOptions { get; } + public IEnumerable Filters { get; set; } + protected IEnumerable GetItemsOnCurrentPage(IList items) { if (items.Count > itemsPerPage) diff --git a/DigitalLearningSolutions.Web/ViewModels/Common/SearchablePage/CurrentFiltersViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Common/SearchablePage/CurrentFiltersViewModel.cs new file mode 100644 index 0000000000..3b5723ee03 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/Common/SearchablePage/CurrentFiltersViewModel.cs @@ -0,0 +1,17 @@ +namespace DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage +{ + using System.Collections.Generic; + + public class CurrentFiltersViewModel + { + public CurrentFiltersViewModel(IEnumerable filters, string? searchString) + { + AppliedFilters = filters; + SearchString = searchString; + } + + public IEnumerable AppliedFilters { get; set; } + + public string? SearchString { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/Common/SearchablePage/FilterOptionViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Common/SearchablePage/FilterOptionViewModel.cs new file mode 100644 index 0000000000..7fab70fc61 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/Common/SearchablePage/FilterOptionViewModel.cs @@ -0,0 +1,20 @@ +namespace DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage +{ + using DigitalLearningSolutions.Web.Models.Enums; + + public class FilterOptionViewModel + { + public FilterOptionViewModel(string displayText, string filterValue, FilterStatus tagStatus) + { + DisplayText = displayText; + FilterValue = filterValue; + TagStatus = tagStatus; + } + + public string DisplayText { get; set; } + + public string FilterValue { get; set; } + + public FilterStatus TagStatus { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/Common/SearchablePage/FilterViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Common/SearchablePage/FilterViewModel.cs new file mode 100644 index 0000000000..d08b6a6c62 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/Common/SearchablePage/FilterViewModel.cs @@ -0,0 +1,24 @@ +namespace DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage +{ + using System.Collections.Generic; + + public class FilterViewModel + { + public FilterViewModel( + string filterProperty, + string filterName, + IEnumerable filterOptions + ) + { + FilterProperty = filterProperty; + FilterName = filterName; + FilterOptions = filterOptions; + } + + public string FilterProperty { get; set; } + + public string FilterName { get; set; } + + public IEnumerable FilterOptions { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/Common/SearchablePage/SearchableTagViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Common/SearchablePage/SearchableTagViewModel.cs new file mode 100644 index 0000000000..bae5aed742 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/Common/SearchablePage/SearchableTagViewModel.cs @@ -0,0 +1,30 @@ +namespace DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage +{ + using DigitalLearningSolutions.Web.Models.Enums; + + public class SearchableTagViewModel : FilterOptionViewModel + { + public SearchableTagViewModel(FilterOptionViewModel filterOption, bool hidden = false) + : base( + filterOption.DisplayText, + filterOption.FilterValue, + filterOption.TagStatus + ) + { + Hidden = hidden; + } + + public bool Hidden { get; set; } + + public string NhsTagStyle() + { + return TagStatus switch + { + FilterStatus.Default => "nhsuk-tag--grey", + FilterStatus.Warning => "nhsuk-tag--red", + FilterStatus.Success => "nhsuk-tag--green", + _ => string.Empty + }; + } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/Common/CentreContactInfoViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/CentreContactInfoViewModel.cs similarity index 83% rename from DigitalLearningSolutions.Web/ViewModels/Common/CentreContactInfoViewModel.cs rename to DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/CentreContactInfoViewModel.cs index b4fbc8b75b..6d7d90558e 100644 --- a/DigitalLearningSolutions.Web/ViewModels/Common/CentreContactInfoViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/CentreContactInfoViewModel.cs @@ -1,4 +1,4 @@ -namespace DigitalLearningSolutions.Web.ViewModels.Common +namespace DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents { using System.Collections.Generic; diff --git a/DigitalLearningSolutions.Web/ViewModels/Common/ErrorSummaryViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/ErrorSummaryViewModel.cs similarity index 88% rename from DigitalLearningSolutions.Web/ViewModels/Common/ErrorSummaryViewModel.cs rename to DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/ErrorSummaryViewModel.cs index 4acfee81cf..9ece78419c 100644 --- a/DigitalLearningSolutions.Web/ViewModels/Common/ErrorSummaryViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/ErrorSummaryViewModel.cs @@ -1,4 +1,4 @@ -namespace DigitalLearningSolutions.Web.ViewModels.Common +namespace DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents { using System.Collections.Generic; diff --git a/DigitalLearningSolutions.Web/ViewModels/Common/FileInputViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/FileInputViewModel.cs similarity index 92% rename from DigitalLearningSolutions.Web/ViewModels/Common/FileInputViewModel.cs rename to DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/FileInputViewModel.cs index f516a1c949..a84372f33d 100644 --- a/DigitalLearningSolutions.Web/ViewModels/Common/FileInputViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/FileInputViewModel.cs @@ -1,4 +1,4 @@ -namespace DigitalLearningSolutions.Web.ViewModels.Common +namespace DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents { public class FileInputViewModel { diff --git a/DigitalLearningSolutions.Web/ViewModels/Common/LinkViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/LinkViewModel.cs similarity index 85% rename from DigitalLearningSolutions.Web/ViewModels/Common/LinkViewModel.cs rename to DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/LinkViewModel.cs index 078a3e6ea2..f8ae8d3342 100644 --- a/DigitalLearningSolutions.Web/ViewModels/Common/LinkViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/LinkViewModel.cs @@ -1,4 +1,4 @@ -namespace DigitalLearningSolutions.Web.ViewModels.Common +namespace DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents { using System.Collections.Generic; diff --git a/DigitalLearningSolutions.Web/ViewModels/Common/LogoViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/LogoViewModel.cs similarity index 74% rename from DigitalLearningSolutions.Web/ViewModels/Common/LogoViewModel.cs rename to DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/LogoViewModel.cs index 316bb13db1..261dba9ab0 100644 --- a/DigitalLearningSolutions.Web/ViewModels/Common/LogoViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/LogoViewModel.cs @@ -1,4 +1,4 @@ -namespace DigitalLearningSolutions.Web.ViewModels.Common +namespace DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents { using DigitalLearningSolutions.Data.Models; diff --git a/DigitalLearningSolutions.Web/ViewModels/Common/NumberOfAdministratorsViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/NumberOfAdministratorsViewModel.cs similarity index 85% rename from DigitalLearningSolutions.Web/ViewModels/Common/NumberOfAdministratorsViewModel.cs rename to DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/NumberOfAdministratorsViewModel.cs index c6ffdab939..8086165350 100644 --- a/DigitalLearningSolutions.Web/ViewModels/Common/NumberOfAdministratorsViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/NumberOfAdministratorsViewModel.cs @@ -1,4 +1,4 @@ -namespace DigitalLearningSolutions.Web.ViewModels.Common +namespace DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents { using System.Collections.Generic; using System.Linq; @@ -14,8 +14,8 @@ public NumberOfAdministratorsViewModel(Centre centreDetails, List adm Supervisors = adminUsers.Count(a => a.IsSupervisor).ToString(); var trainers = adminUsers.Count(a => a.IsTrainer); - var cmsAdministrators = adminUsers.Count(a => a.ImportOnly); - var cmsManagers = adminUsers.Count(a => a.IsContentManager) - cmsAdministrators; + var cmsAdministrators = adminUsers.Count(a => a.IsCmsAdministrator); + var cmsManagers = adminUsers.Count(a => a.IsCmsManager); var ccLicences = adminUsers.Count(a => a.IsContentCreator); Trainers = DisplayStringHelper.FormatNumberWithLimit(trainers, centreDetails.TrainerSpots); diff --git a/DigitalLearningSolutions.Web/ViewModels/Common/SearchBoxViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/SearchBoxViewModel.cs similarity index 67% rename from DigitalLearningSolutions.Web/ViewModels/Common/SearchBoxViewModel.cs rename to DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/SearchBoxViewModel.cs index 68838b8532..63e9878e75 100644 --- a/DigitalLearningSolutions.Web/ViewModels/Common/SearchBoxViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/SearchBoxViewModel.cs @@ -1,23 +1,28 @@ -namespace DigitalLearningSolutions.Web.ViewModels.Common +namespace DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents { + using DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage; + public class SearchBoxViewModel { public readonly string AspAction; public readonly string AspController; public readonly BaseSearchablePageViewModel SearchablePageViewModel; public readonly string Label; + public readonly string? Class; public SearchBoxViewModel( string aspController, string aspAction, BaseSearchablePageViewModel searchablePageViewModel, - string label + string label, + string? cssClass ) { AspAction = aspAction; AspController = aspController; SearchablePageViewModel = searchablePageViewModel; Label = label; + Class = cssClass; } } } diff --git a/DigitalLearningSolutions.Web/ViewModels/Common/SelectListViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/SelectListViewModel.cs similarity index 92% rename from DigitalLearningSolutions.Web/ViewModels/Common/SelectListViewModel.cs rename to DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/SelectListViewModel.cs index badac593f0..39f86a278d 100644 --- a/DigitalLearningSolutions.Web/ViewModels/Common/SelectListViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/SelectListViewModel.cs @@ -1,4 +1,4 @@ -namespace DigitalLearningSolutions.Web.ViewModels.Common +namespace DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents { using System.Collections.Generic; using Microsoft.AspNetCore.Mvc.Rendering; diff --git a/DigitalLearningSolutions.Web/ViewModels/Common/SideMenuLinkViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/SideMenuLinkViewModel.cs similarity index 84% rename from DigitalLearningSolutions.Web/ViewModels/Common/SideMenuLinkViewModel.cs rename to DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/SideMenuLinkViewModel.cs index 31a0df1e00..b35806923d 100644 --- a/DigitalLearningSolutions.Web/ViewModels/Common/SideMenuLinkViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/SideMenuLinkViewModel.cs @@ -1,4 +1,4 @@ -namespace DigitalLearningSolutions.Web.ViewModels.Common +namespace DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents { using System.Collections.Generic; diff --git a/DigitalLearningSolutions.Web/ViewModels/Common/SideMenuLinkWithHrefViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/SideMenuLinkWithHrefViewModel.cs similarity index 86% rename from DigitalLearningSolutions.Web/ViewModels/Common/SideMenuLinkWithHrefViewModel.cs rename to DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/SideMenuLinkWithHrefViewModel.cs index 2697cb7415..5c852945b4 100644 --- a/DigitalLearningSolutions.Web/ViewModels/Common/SideMenuLinkWithHrefViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/SideMenuLinkWithHrefViewModel.cs @@ -1,4 +1,4 @@ -namespace DigitalLearningSolutions.Web.ViewModels.Common +namespace DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents { using System.Collections.Generic; diff --git a/DigitalLearningSolutions.Web/ViewModels/Common/TermsConditionsViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/TermsConditionsViewModel.cs similarity index 85% rename from DigitalLearningSolutions.Web/ViewModels/Common/TermsConditionsViewModel.cs rename to DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/TermsConditionsViewModel.cs index 75995fed0c..26c24583e0 100644 --- a/DigitalLearningSolutions.Web/ViewModels/Common/TermsConditionsViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/TermsConditionsViewModel.cs @@ -1,4 +1,4 @@ -namespace DigitalLearningSolutions.Web.ViewModels.Common +namespace DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents { public class TermsConditionsViewModel { diff --git a/DigitalLearningSolutions.Web/ViewModels/Common/TextAreaViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/TextAreaViewModel.cs similarity index 91% rename from DigitalLearningSolutions.Web/ViewModels/Common/TextAreaViewModel.cs rename to DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/TextAreaViewModel.cs index dc5da79ee9..f8ea0e99a3 100644 --- a/DigitalLearningSolutions.Web/ViewModels/Common/TextAreaViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/TextAreaViewModel.cs @@ -1,4 +1,4 @@ -namespace DigitalLearningSolutions.Web.ViewModels.Common +namespace DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents { using System.Collections.Generic; using System.Linq; diff --git a/DigitalLearningSolutions.Web/ViewModels/Common/TextInputViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/TextInputViewModel.cs similarity index 91% rename from DigitalLearningSolutions.Web/ViewModels/Common/TextInputViewModel.cs rename to DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/TextInputViewModel.cs index e06be69e16..72c97aefff 100644 --- a/DigitalLearningSolutions.Web/ViewModels/Common/TextInputViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/Common/ViewComponents/TextInputViewModel.cs @@ -1,4 +1,4 @@ -namespace DigitalLearningSolutions.Web.ViewModels.Common +namespace DigitalLearningSolutions.Web.ViewModels.Common.ViewComponents { using System.Collections.Generic; using System.Linq; diff --git a/DigitalLearningSolutions.Web/ViewModels/Frameworks/AllFrameworksViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Frameworks/AllFrameworksViewModel.cs index c39b66285a..16867d4068 100644 --- a/DigitalLearningSolutions.Web/ViewModels/Frameworks/AllFrameworksViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/Frameworks/AllFrameworksViewModel.cs @@ -4,7 +4,7 @@ using System.Linq; using DigitalLearningSolutions.Data.Models.Frameworks; using DigitalLearningSolutions.Web.Helpers; - using DigitalLearningSolutions.Web.ViewModels.Common; + using DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage; public class AllFrameworksViewModel : BaseSearchablePageViewModel { @@ -16,7 +16,7 @@ public AllFrameworksViewModel( string sortBy, string sortDirection, int page - ) : base(searchString, sortBy, sortDirection, page, 12) + ) : base(searchString, sortBy, sortDirection, page, itemsPerPage: 12) { var sortedItems = GenericSortingHelper.SortAllItems( brandedFrameworks.AsQueryable(), diff --git a/DigitalLearningSolutions.Web/ViewModels/Frameworks/MyFrameworksViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Frameworks/MyFrameworksViewModel.cs index 2a5f9ee530..0dbe8f4923 100644 --- a/DigitalLearningSolutions.Web/ViewModels/Frameworks/MyFrameworksViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/Frameworks/MyFrameworksViewModel.cs @@ -4,7 +4,7 @@ using System.Linq; using DigitalLearningSolutions.Data.Models.Frameworks; using DigitalLearningSolutions.Web.Helpers; - using DigitalLearningSolutions.Web.ViewModels.Common; + using DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage; public class MyFrameworksViewModel : BaseSearchablePageViewModel { @@ -18,7 +18,7 @@ public MyFrameworksViewModel( string sortDirection, int page, bool isFrameworkDeveloper - ) : base(searchString, sortBy, sortDirection, page, 12) + ) : base(searchString, sortBy, sortDirection, page, itemsPerPage: 12) { var sortedItems = GenericSortingHelper.SortAllItems( brandedFrameworks.AsQueryable(), diff --git a/DigitalLearningSolutions.Web/ViewModels/LearningPortal/Available/AvailablePageViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/LearningPortal/Available/AvailablePageViewModel.cs index 004e05d550..f92dcf6d8e 100644 --- a/DigitalLearningSolutions.Web/ViewModels/LearningPortal/Available/AvailablePageViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/LearningPortal/Available/AvailablePageViewModel.cs @@ -4,7 +4,7 @@ using System.Linq; using DigitalLearningSolutions.Data.Models.Courses; using DigitalLearningSolutions.Web.Helpers; - using DigitalLearningSolutions.Web.ViewModels.Common; + using DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage; public class AvailablePageViewModel : BaseSearchablePageViewModel { diff --git a/DigitalLearningSolutions.Web/ViewModels/LearningPortal/Completed/CompletedPageViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/LearningPortal/Completed/CompletedPageViewModel.cs index 7659dc5f20..7dd14d1581 100644 --- a/DigitalLearningSolutions.Web/ViewModels/LearningPortal/Completed/CompletedPageViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/LearningPortal/Completed/CompletedPageViewModel.cs @@ -4,7 +4,7 @@ namespace DigitalLearningSolutions.Web.ViewModels.LearningPortal.Completed using System.Linq; using DigitalLearningSolutions.Data.Models.Courses; using DigitalLearningSolutions.Web.Helpers; - using DigitalLearningSolutions.Web.ViewModels.Common; + using DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage; using Microsoft.Extensions.Configuration; public class CompletedPageViewModel : BaseSearchablePageViewModel diff --git a/DigitalLearningSolutions.Web/ViewModels/LearningPortal/Current/CurrentPageViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/LearningPortal/Current/CurrentPageViewModel.cs index a405f31f99..604750ce14 100644 --- a/DigitalLearningSolutions.Web/ViewModels/LearningPortal/Current/CurrentPageViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/LearningPortal/Current/CurrentPageViewModel.cs @@ -6,7 +6,7 @@ namespace DigitalLearningSolutions.Web.ViewModels.LearningPortal.Current using DigitalLearningSolutions.Data.Models.Courses; using DigitalLearningSolutions.Data.Models.SelfAssessments; using DigitalLearningSolutions.Web.Helpers; - using DigitalLearningSolutions.Web.ViewModels.Common; + using DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage; using DigitalLearningSolutions.Web.ViewModels.LearningPortal.SelfAssessments; public class CurrentPageViewModel : BaseSearchablePageViewModel diff --git a/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Centre/Administrator/CentreAdministratorsViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Centre/Administrator/CentreAdministratorsViewModel.cs index 3b59311a98..4fcde8e47a 100644 --- a/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Centre/Administrator/CentreAdministratorsViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Centre/Administrator/CentreAdministratorsViewModel.cs @@ -4,18 +4,37 @@ using System.Linq; using DigitalLearningSolutions.Data.Models.User; using DigitalLearningSolutions.Web.Helpers; - using DigitalLearningSolutions.Web.ViewModels.Common; + using DigitalLearningSolutions.Web.Models.Enums; + using DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage; public class CentreAdministratorsViewModel : BaseSearchablePageViewModel { + private static readonly IEnumerable RoleOptions = new[] + { + AdminFilterOptions.CentreAdministrator, + AdminFilterOptions.Supervisor, + AdminFilterOptions.Trainer, + AdminFilterOptions.ContentCreatorLicense, + AdminFilterOptions.CmsAdministrator, + AdminFilterOptions.CmsManager + }; + + private static readonly IEnumerable AccountStatusOptions = new[] + { + AdminFilterOptions.IsLocked, + AdminFilterOptions.IsNotLocked + }; + public CentreAdministratorsViewModel( int centreId, IEnumerable adminUsers, + IEnumerable categories, string? searchString, string sortBy, string sortDirection, + string? filterString, int page - ) : base(searchString, sortBy, sortDirection, page) + ) : base(searchString, sortBy, sortDirection, page, filterString) { CentreId = centreId; var sortedItems = GenericSortingHelper.SortAllItems( @@ -23,11 +42,23 @@ int page sortBy, sortDirection ); - var searchedItems = GenericSearchHelper.SearchItems(sortedItems, SearchString).ToList(); - MatchingSearchResults = searchedItems.Count; + var searchedItems = GenericSearchHelper.SearchItems(sortedItems, SearchString); + var filteredItems = FilteringHelper.FilterItems(searchedItems.AsQueryable(), filterString).ToList(); + MatchingSearchResults = filteredItems.Count; SetTotalPages(); - var paginatedItems = GetItemsOnCurrentPage(searchedItems); + var paginatedItems = GetItemsOnCurrentPage(filteredItems); Admins = paginatedItems.Select(adminUser => new SearchableAdminViewModel(adminUser)); + IEnumerable categoryOptions = + categories.Select( + c => new FilterOptionViewModel(c, $"{nameof(AdminUser.CategoryName)}|{c}", FilterStatus.Default) + ); + + Filters = new[] + { + new FilterViewModel("Role", "Role", RoleOptions), + new FilterViewModel("CategoryName", "Category", categoryOptions), + new FilterViewModel("AccountStatus", "Account Status", AccountStatusOptions) + }; } public int CentreId { get; set; } diff --git a/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Centre/Administrator/SearchableAdminViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Centre/Administrator/SearchableAdminViewModel.cs index 627903f7c2..e056dadc38 100644 --- a/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Centre/Administrator/SearchableAdminViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Centre/Administrator/SearchableAdminViewModel.cs @@ -1,10 +1,10 @@ namespace DigitalLearningSolutions.Web.ViewModels.TrackingSystem.Centre.Administrator { - using System.Collections.Generic; using DigitalLearningSolutions.Data.Models.User; using DigitalLearningSolutions.Web.Helpers; + using DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage; - public class SearchableAdminViewModel + public class SearchableAdminViewModel : BaseFilterableViewModel { public SearchableAdminViewModel(AdminUser adminUser) { @@ -25,7 +25,5 @@ public SearchableAdminViewModel(AdminUser adminUser) public string? EmailAddress { get; set; } public bool IsLocked { get; set; } - - public IEnumerable<(string, string)> Tags { get; set; } } } diff --git a/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Centre/ContractDetails/ContractDetailsViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Centre/ContractDetails/ContractDetailsViewModel.cs index 5ab8121619..f7272294e2 100644 --- a/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Centre/ContractDetails/ContractDetailsViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Centre/ContractDetails/ContractDetailsViewModel.cs @@ -14,8 +14,8 @@ public ContractDetailsViewModel(List adminUsers, Centre centreDetails Supervisors = adminUsers.Count(a => a.IsSupervisor).ToString(); var trainers = adminUsers.Count(a => a.IsTrainer); - var cmsAdministrators = adminUsers.Count(a => a.ImportOnly); - var cmsManagers = adminUsers.Count(a => a.IsContentManager) - cmsAdministrators; + var cmsAdministrators = adminUsers.Count(a => a.IsCmsAdministrator); + var cmsManagers = adminUsers.Count(a => a.IsCmsManager); var contentCreators = adminUsers.Count(a => a.IsContentCreator); Trainers = DisplayStringHelper.FormatNumberWithLimit(trainers, centreDetails.TrainerSpots); diff --git a/DigitalLearningSolutions.Web/Views/Frameworks/Shared/_FrameworkPageSearch.cshtml b/DigitalLearningSolutions.Web/Views/Frameworks/Shared/_FrameworkPageSearch.cshtml index a1508f13de..c547190bf6 100644 --- a/DigitalLearningSolutions.Web/Views/Frameworks/Shared/_FrameworkPageSearch.cshtml +++ b/DigitalLearningSolutions.Web/Views/Frameworks/Shared/_FrameworkPageSearch.cshtml @@ -1,4 +1,4 @@ -@using DigitalLearningSolutions.Web.ViewModels.Common +@using DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage @model BaseSearchablePageViewModel