Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
9417bb4
HEEDLS-490 Create AllDelegates controller with CentreAdmin policy; cr…
ibrahimmunir14 Jun 30, 2021
34711fe
HEEDLS-490 Link to AllDelegates from nav bar, nav sidebar and centre …
ibrahimmunir14 Jun 30, 2021
9384f41
HEEDLS-490 Add register/email/upload/download buttons and style
ibrahimmunir14 Jun 30, 2021
e5155cc
HEEDLS-490 Create AllDelegates and SearchableDelegate view models
ibrahimmunir14 Jun 30, 2021
0beed4d
HEEDLS-490 Create SearchableDelegateCard view; update Index to displa…
ibrahimmunir14 Jun 30, 2021
e7231bd
HEEDLS-490 Update controller to fetch and display delegate cards
ibrahimmunir14 Jun 30, 2021
6d7adec
HEEDLS-490 Create DelegateUserCard subclass; update dataservice metho…
ibrahimmunir14 Jul 1, 2021
8b29325
HEEDLS-490 Add SelfReg/Active/Admin/PasswordSet tags to DelegateCard
ibrahimmunir14 Jul 1, 2021
f2bcd95
HEEDLS-490 Add Name/Id/Email/RegDate/JobGroup to DelegateCard
ibrahimmunir14 Jul 1, 2021
4338e46
HEEDLS-490 Add buttons at bottom of DelegateCard
ibrahimmunir14 Jul 1, 2021
01829f9
HEEDLS-490 Add custom prompt fields to DelegateCard
ibrahimmunir14 Jul 1, 2021
ed2214f
HEEDLS-490 Fix bug with admin tag; change tag colour and ordering
ibrahimmunir14 Jul 5, 2021
3e40f39
HEEDLS-490 Use styled links instead of buttons; update sass; remove r…
ibrahimmunir14 Jul 6, 2021
3234e80
HEEDLS-490 Change tag colours, HeaderPath, and other minor changes
ibrahimmunir14 Jul 6, 2021
11fc107
HEEDLS-490 Move tag name logic into viewmodel; test
ibrahimmunir14 Jul 6, 2021
7dd943a
HEEDLS-490 Perform DelegateUserCard to SearchableDelegateViewModels m…
ibrahimmunir14 Jul 6, 2021
e522ddb
HEEDLS-490 Minor refactor and add comments in SearchableDelegateViewM…
ibrahimmunir14 Jul 6, 2021
c159ea1
HEEDLS-490 Write tests for GetDelegateUserCardsByCentreId; add AllDel…
ibrahimmunir14 Jul 6, 2021
ee1292a
HEEDLS-490 Inline reg status/admin tag css strings
ibrahimmunir14 Jul 6, 2021
2f846f2
HEEDLS-490 Fix SearchableDelegateViewModel date test
ibrahimmunir14 Jul 6, 2021
afba14c
HEEDLS-490 Rename test for clarity
ibrahimmunir14 Jul 7, 2021
f67b40a
Merge branch 'master' into HEEDLS-490-all-delegates-display-cards
ibrahimmunir14 Jul 7, 2021
1c483ae
HEEDLS-490 Display date string in en-gb format
ibrahimmunir14 Jul 7, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -367,5 +367,40 @@ public void GetNumberOfActiveApprovedDelegatesAtCentre_returns_expected_count()
// Then
count.Should().Be(3420);
}

[Test]
public void GetDelegateUserCardsByCentreId_populates_DelegateUser_fields_correctly()
{
// Given
var expected = UserTestHelper.GetDefaultDelegateUser(
dateRegistered: DateTime.Parse("2010-09-22 06:52:09.080"),
jobGroupName: "Nursing / midwifery"
);

// When
var userCards = userDataService.GetDelegateUserCardsByCentreId(2);

// Then
var userCard = userCards.Single(user => user.Id == 2);
userCard.Should().BeEquivalentTo(expected);
userCard.Active.Should().BeTrue();
userCard.SelfReg.Should().BeFalse();
userCard.ExternalReg.Should().BeFalse();
userCard.AdminId.Should().BeNull();
}

[Test]
public void GetDelegateUserCardsByCentreId_populates_DelegateUserCard_admin_fields_correctly()
{
// When
var userCards = userDataService.GetDelegateUserCardsByCentreId(279);

// Then
var userCard = userCards.Single(user => user.Id == 97055);
userCard.Active.Should().BeTrue();
userCard.SelfReg.Should().BeTrue();
userCard.ExternalReg.Should().BeFalse();
userCard.AdminId.Should().Be(74);
}
}
}
38 changes: 38 additions & 0 deletions DigitalLearningSolutions.Data/DataServices/UserDataService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public interface IUserDataService
public AdminUser? GetAdminUserById(int id);
public DelegateUser? GetDelegateUserById(int id);
public List<AdminUser> GetAdminUsersByCentreId(int centreId);
public List<DelegateUserCard> GetDelegateUserCardsByCentreId(int centreId);
public AdminUser? GetAdminUserByUsername(string username);
public List<DelegateUser> GetDelegateUsersByUsername(string username);
public AdminUser? GetAdminUserByEmailAddress(string emailAddress);
Expand Down Expand Up @@ -175,6 +176,43 @@ FROM AdminUsers AS au
return users;
}

public List<DelegateUserCard> GetDelegateUserCardsByCentreId(int centreId)
{
return connection.Query<DelegateUserCard>(
@"SELECT
cd.CandidateID AS Id,
cd.CandidateNumber,
ct.CentreName,
cd.CentreID,
cd.DateRegistered,
ct.Active AS CentreActive,
cd.EmailAddress,
cd.FirstName,
cd.LastName,
cd.Password,
cd.Approved,
cd.Answer1,
cd.Answer2,
cd.Answer3,
cd.Answer4,
cd.Answer5,
cd.Answer6,
jg.JobGroupName,
cd.SelfReg,
cd.ExternalReg,
cd.Active,
(SELECT AdminID
FROM AdminUsers au
WHERE au.Email = cd.EmailAddress AND au.CentreID = cd.CentreID
) AS AdminID
FROM Candidates AS cd
INNER JOIN Centres AS ct ON ct.CentreID = cd.CentreID
INNER JOIN JobGroups AS jg ON jg.JobGroupID = cd.JobGroupID
WHERE cd.CentreId = @centreId AND cd.Approved = 1",
new { centreId }
).ToList();
}

public AdminUser? GetAdminUserByUsername(string username)
{
var user = connection.Query<AdminUser>(
Expand Down
10 changes: 10 additions & 0 deletions DigitalLearningSolutions.Data/Models/User/DelegateUserCard.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace DigitalLearningSolutions.Data.Models.User
{
public class DelegateUserCard : DelegateUser
{
public bool SelfReg { get; set; }
public bool ExternalReg { get; set; }
public bool Active { get; set; }
public int? AdminId { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -1,56 +1,57 @@
namespace DigitalLearningSolutions.Web.AutomatedUiTests.AccessibilityTests
{
using DigitalLearningSolutions.Web.AutomatedUiTests.TestHelpers;
using Xunit;

public class BasicAccessibilityTests : AccessibilityTestsBase
{
public BasicAccessibilityTests(SeleniumServerFactory<Startup> factory) : base(factory) { }

[Theory]
[InlineData("/Home/Welcome", "Welcome - Digital Learning Solutions")]
[InlineData("/Home/Products", "Products - Digital Learning Solutions")]
[InlineData("/Home/LearningContent", "Learning Content - Digital Learning Solutions")]
[InlineData("/Login", "Log in")]
[InlineData("/ForgotPassword", "Reset your password")]
[InlineData("/ResetPassword/Error", "Something went wrong...")]
public void Page_has_no_accessibility_errors(string url, string pageTitle)
{
// when
Driver.Navigate().GoToUrl(BaseUrl + url);

// then
AnalyzePageHeadingAndAccessibility(pageTitle);
}

[Theory]
[InlineData("/MyAccount", "My account")]
[InlineData("/MyAccount/EditDetails", "Edit details")]
[InlineData("/TrackingSystem/Centre/Administrators", "Centre administrators")]
[InlineData("/TrackingSystem/Centre/Dashboard", "Centre dashboard")]
[InlineData("/TrackingSystem/Centre/Ranking", "Centre ranking")]
[InlineData("/TrackingSystem/Centre/ContractDetails", "Contract details")]
[InlineData("/TrackingSystem/CentreConfiguration", "Centre configuration")]
[InlineData("/TrackingSystem/CentreConfiguration/EditCentreManagerDetails", "Edit centre manager details")]
[InlineData("/TrackingSystem/CentreConfiguration/EditCentreWebsiteDetails", "Edit centre content on DLS website")]
[InlineData("/TrackingSystem/CentreConfiguration/RegistrationPrompts", "Manage delegate registration prompts")]
[InlineData("/TrackingSystem/CentreConfiguration/RegistrationPrompts/1/Remove", "Remove delegate registration prompt")]
[InlineData("/TrackingSystem/Centre/Reports", "Centre reports")]
[InlineData("/TrackingSystem/Centre/TopCourses", "Top courses")]
[InlineData("/TrackingSystem/Delegates/Approve", "Approve delegate registrations")]
[InlineData("/NotificationPreferences", "Notification preferences")]
[InlineData("/NotificationPreferences/Edit/AdminUser", "Update notification preferences")]
[InlineData("/NotificationPreferences/Edit/DelegateUser", "Update notification preferences")]
[InlineData("/ChangePassword", "Change password")]
[InlineData("/TrackingSystem/Support", "Support")]
public void Authenticated_page_has_no_accessibility_errors(string url, string pageTitle)
{
// when
Driver.LogUserInAsAdminAndDelegate(BaseUrl);
Driver.Navigate().GoToUrl(BaseUrl + url);

// then
AnalyzePageHeadingAndAccessibility(pageTitle);
}
}
}
namespace DigitalLearningSolutions.Web.AutomatedUiTests.AccessibilityTests
{
using DigitalLearningSolutions.Web.AutomatedUiTests.TestHelpers;
using Xunit;

public class BasicAccessibilityTests : AccessibilityTestsBase
{
public BasicAccessibilityTests(SeleniumServerFactory<Startup> factory) : base(factory) { }

[Theory]
[InlineData("/Home/Welcome", "Welcome - Digital Learning Solutions")]
[InlineData("/Home/Products", "Products - Digital Learning Solutions")]
[InlineData("/Home/LearningContent", "Learning Content - Digital Learning Solutions")]
[InlineData("/Login", "Log in")]
[InlineData("/ForgotPassword", "Reset your password")]
[InlineData("/ResetPassword/Error", "Something went wrong...")]
public void Page_has_no_accessibility_errors(string url, string pageTitle)
{
// when
Driver.Navigate().GoToUrl(BaseUrl + url);

// then
AnalyzePageHeadingAndAccessibility(pageTitle);
}

[Theory]
[InlineData("/MyAccount", "My account")]
[InlineData("/MyAccount/EditDetails", "Edit details")]
[InlineData("/TrackingSystem/Centre/Administrators", "Centre administrators")]
[InlineData("/TrackingSystem/Centre/Dashboard", "Centre dashboard")]
[InlineData("/TrackingSystem/Centre/Ranking", "Centre ranking")]
[InlineData("/TrackingSystem/Centre/ContractDetails", "Contract details")]
[InlineData("/TrackingSystem/CentreConfiguration", "Centre configuration")]
[InlineData("/TrackingSystem/CentreConfiguration/EditCentreManagerDetails", "Edit centre manager details")]
[InlineData("/TrackingSystem/CentreConfiguration/EditCentreWebsiteDetails", "Edit centre content on DLS website")]
[InlineData("/TrackingSystem/CentreConfiguration/RegistrationPrompts", "Manage delegate registration prompts")]
[InlineData("/TrackingSystem/CentreConfiguration/RegistrationPrompts/1/Remove", "Remove delegate registration prompt")]
[InlineData("/TrackingSystem/Centre/Reports", "Centre reports")]
[InlineData("/TrackingSystem/Delegates/All", "Delegates")]
[InlineData("/TrackingSystem/Centre/TopCourses", "Top courses")]
[InlineData("/TrackingSystem/Delegates/Approve", "Approve delegate registrations")]
[InlineData("/NotificationPreferences", "Notification preferences")]
[InlineData("/NotificationPreferences/Edit/AdminUser", "Update notification preferences")]
[InlineData("/NotificationPreferences/Edit/DelegateUser", "Update notification preferences")]
[InlineData("/ChangePassword", "Change password")]
[InlineData("/TrackingSystem/Support", "Support")]
public void Authenticated_page_has_no_accessibility_errors(string url, string pageTitle)
{
// when
Driver.LogUserInAsAdminAndDelegate(BaseUrl);
Driver.Navigate().GoToUrl(BaseUrl + url);

// then
AnalyzePageHeadingAndAccessibility(pageTitle);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
namespace DigitalLearningSolutions.Web.Tests.ViewModels.TrackingSystem.Delegates.AllDelegates
{
using System;
using System.Collections.Generic;
using DigitalLearningSolutions.Data.Models.User;
using DigitalLearningSolutions.Web.ViewModels.Common;
using DigitalLearningSolutions.Web.ViewModels.TrackingSystem.Delegates;
using FluentAssertions;
using NUnit.Framework;

public class SearchableDelegateViewModelTests
{
private readonly List<CustomFieldViewModel> customFields = new List<CustomFieldViewModel>();

[Test]
public void SearchableDelegateViewModel_sets_active_tag_name_correctly()
{
// Given
var activeUser = new DelegateUserCard { Active = true };
var inactiveUser = new DelegateUserCard { Active = false };

// When
var activeModel = new SearchableDelegateViewModel(activeUser, customFields);
var inactiveModel = new SearchableDelegateViewModel(inactiveUser, customFields);

// Then
activeModel.ActiveTagName.Should().Be("Active");
inactiveModel.ActiveTagName.Should().Be("Inactive");
}

[Test]
public void SearchableDelegateViewModel_sets_password_tag_name_correctly()
{
// Given
var pwSetUser = new DelegateUserCard { Password = "pw" };
var pwNotSetUser = new DelegateUserCard { Password = null };

// When
var pwSetModel = new SearchableDelegateViewModel(pwSetUser, customFields);
var pwNotSetModel = new SearchableDelegateViewModel(pwNotSetUser, customFields);

// Then
pwSetModel.PasswordTagName.Should().Be("Password set");
pwNotSetModel.PasswordTagName.Should().Be("Password not set");
}

[Test]
public void SearchableDelegateViewModel_sets_regstatus_tag_name_correctly()
{
// Given
var selfRegUser = new DelegateUserCard { SelfReg = true, ExternalReg = false };
var selfRegExternalUser = new DelegateUserCard { SelfReg = true, ExternalReg = true };
var centreRegUser = new DelegateUserCard { SelfReg = false };

// When
var selfRegModel = new SearchableDelegateViewModel(selfRegUser, customFields);
var selfRegExternalModel = new SearchableDelegateViewModel(selfRegExternalUser, customFields);
var centreRegModel = new SearchableDelegateViewModel(centreRegUser, customFields);

// Then
selfRegModel.RegStatusTagName.Should().Be("Self registered");
selfRegExternalModel.RegStatusTagName.Should().Be("Self registered (External)");
centreRegModel.RegStatusTagName.Should().Be("Registered by centre");
}

[Test]
public void SearchableDelegateViewModel_sets_reg_date_string_correctly()
{
// Given
var date = new DateTime(2021, 05, 13);
var user = new DelegateUserCard { DateRegistered = date };

// When
var model = new SearchableDelegateViewModel(user, customFields);

// Then
model.RegistrationDate.Should().Be("13/05/2021");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
namespace DigitalLearningSolutions.Web.Controllers.TrackingSystem.Delegates
{
using System.Linq;
using DigitalLearningSolutions.Data.DataServices;
using DigitalLearningSolutions.Web.Helpers;
using DigitalLearningSolutions.Web.ViewModels.TrackingSystem.Delegates;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

[Authorize(Policy = CustomPolicies.UserCentreAdmin)]
[Route("TrackingSystem/Delegates/All")]
public class AllDelegatesController : Controller
{
private readonly CustomPromptHelper customPromptHelper;
private readonly IUserDataService userDataService;

public AllDelegatesController(IUserDataService userDataService, CustomPromptHelper customPromptHelper)
{
this.userDataService = userDataService;
this.customPromptHelper = customPromptHelper;
}

public IActionResult Index()
{
var centreId = User.GetCentreId();
var delegateUsers = userDataService.GetDelegateUserCardsByCentreId(centreId).Take(10);
var searchableDelegateViewModels = delegateUsers.Select(
delegateUser =>
{
var customFields = customPromptHelper.GetCustomFieldViewModelsForCentre(centreId, delegateUser);
return new SearchableDelegateViewModel(delegateUser, customFields);
}
);
var model = new AllDelegatesViewModel(centreId, searchableDelegateViewModels);

return View(model);
}
}
}
14 changes: 14 additions & 0 deletions DigitalLearningSolutions.Web/Helpers/CustomPromptHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{
using System.Collections.Generic;
using System.Linq;
using DigitalLearningSolutions.Data.Models.User;
using DigitalLearningSolutions.Data.Services;
using DigitalLearningSolutions.Web.ViewModels.Common;
using Microsoft.AspNetCore.Mvc.ModelBinding;
Expand Down Expand Up @@ -62,6 +63,19 @@ public List<CustomFieldViewModel> GetCustomFieldViewModelsForCentre(
).ToList();
}

public List<CustomFieldViewModel> GetCustomFieldViewModelsForCentre(int centreId, DelegateUser delegateUser)
{
return GetCustomFieldViewModelsForCentre(
centreId,
delegateUser.Answer1,
delegateUser.Answer2,
delegateUser.Answer3,
delegateUser.Answer4,
delegateUser.Answer5,
delegateUser.Answer6
);
}

public void ValidateCustomPrompts(
int centreId,
string? answer1,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
@import "~nhsuk-frontend/packages/core/all";

.all-delegates-button-group {
text-align: right;

@media (max-width: 1024px) {
width: 100%;
text-align: unset;
margin-bottom: nhsuk-spacing(4);
}
}

.all-delegates-button {
padding-top: nhsuk-spacing(2);
padding-bottom: nhsuk-spacing(2);

@media (max-width: 1024px) {
margin-top: nhsuk-spacing(1);
margin-bottom: nhsuk-spacing(1);
}

@media (max-width: 425px) {
width: calc(50% - 4px);
}

@media (max-width: 375px) {
width: 100%;
}
}
Loading