Skip to content

Commit

Permalink
Initial commit of the qualification list page (#231)
Browse files Browse the repository at this point in the history
* Initial commit of the qualification list page. Needs unit tests fixing etc. WIP

* Fixed build and unit tests

* Refactored common logic out into UserJourneyCookieService so it can be shared by multiple controllers

* Added chevron to the view

* Fixed journey tests

* Added new e2e test

* Added aria label to search box and added hover & visited styling to the chevron

* ReSharper & Sonar suggestions.

---------

Co-authored-by: RobertGHippo <[email protected]>
  • Loading branch information
sam-c-dfe and RobertGHippo authored Jul 8, 2024
1 parent 178157b commit 41933b0
Show file tree
Hide file tree
Showing 25 changed files with 634 additions and 153 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,28 @@
using Contentful.Core.Models;

namespace Dfe.EarlyYearsQualification.Content.Entities;

public class QualificationListPage
{
public string Header { get; init; } = string.Empty;

public NavigationLink? BackButton { get; init; }

public string SingleQualificationFoundText { get; init; } = string.Empty;

public string MultipleQualificationsFoundText { get; init; } = string.Empty;

public Document? PreSearchBoxContent { get; init; }

public string SearchButtonText { get; init; } = string.Empty;

public string LevelHeading { get; init; } = string.Empty;

public string AwardingOrganisationHeading { get; init; } = string.Empty;

public Document? PostQualificationListContent { get; init; }

public string SearchCriteriaHeading { get; init; } = string.Empty;

public Document? PostSearchCriteriaContent { get; init; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,13 @@ private static readonly ReadOnlyDictionary<string, int>
public QueryBuilder<Qualification> QueryBuilder { get; init; } = QueryBuilder<Qualification>.New;

public async Task<List<Qualification>> GetFilteredQualifications(int? level, int? startDateMonth,
int? startDateYear)
int? startDateYear, string? awardingOrganisation)
{
logger.LogInformation("Filtering options passed in - level: {Level}, startDateMonth: {StartDateMonth}, startDateYear: {StartDateYear}",
logger.LogInformation("Filtering options passed in - level: {Level}, startDateMonth: {StartDateMonth}, startDateYear: {StartDateYear}, awardingOrganisation: {AwardingOrganisation}",
level,
startDateMonth,
startDateYear);
startDateYear,
awardingOrganisation);

// create query builder
var queryBuilder = QueryBuilder.ContentTypeIs(ContentTypes.Qualification);
Expand All @@ -53,6 +54,11 @@ public async Task<List<Qualification>> GetFilteredQualifications(int? level, int
queryBuilder = queryBuilder.FieldEquals("fields.qualificationLevel", level.Value.ToString());
}

if (!string.IsNullOrEmpty(awardingOrganisation))
{
queryBuilder = queryBuilder.FieldEquals("fields.awardingOrganisationTitle", awardingOrganisation);
}

// get qualifications
ContentfulCollection<Qualification>? qualifications;
try
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ namespace Dfe.EarlyYearsQualification.Content.Services;

public interface IContentFilterService
{
Task<List<Qualification>> GetFilteredQualifications(int? level, int? startDateMonth, int? startDateYear);
Task<List<Qualification>> GetFilteredQualifications(int? level, int? startDateMonth, int? startDateYear, string? awardingOrganisation);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Dfe.EarlyYearsQualification.Mock.Content;

public class MockContentfulFilterService : IContentFilterService
{
public Task<List<Qualification>> GetFilteredQualifications(int? level, int? startDateMonth, int? startDateYear)
public Task<List<Qualification>> GetFilteredQualifications(int? level, int? startDateMonth, int? startDateYear, string? awardingOrganisation)
{
var qualifications =
new List<Qualification>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,16 @@ public Task<List<Qualification>> GetQualifications()
DisplayText = "TEST",
Href = "/questions/what-is-the-awarding-organisation",
OpenInNewTab = false
}
},
LevelHeading = "Level",
AwardingOrganisationHeading = "Awarding organisation",
SearchButtonText = "Refine",
SearchCriteriaHeading = "Your search",
MultipleQualificationsFoundText = "qualifications found",
SingleQualificationFoundText = "qualification found",
PreSearchBoxContent = ContentfulContentHelper.Text("Pre search box content"),
PostQualificationListContent = ContentfulContentHelper.Text("Post qualification list content"),
PostSearchCriteriaContent = ContentfulContentHelper.Text("Post search criteria content")
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using System.Globalization;
using Dfe.EarlyYearsQualification.Content.Entities;
using Dfe.EarlyYearsQualification.Content.Renderers.Entities;
using Dfe.EarlyYearsQualification.Content.Services;
using Dfe.EarlyYearsQualification.Web.Controllers.Base;
using Dfe.EarlyYearsQualification.Web.Models;
using Dfe.EarlyYearsQualification.Web.Models.Content;
using Dfe.EarlyYearsQualification.Web.Services.UserJourneyCookieService;
using Microsoft.AspNetCore.Http.Extensions;
Expand All @@ -14,7 +14,9 @@ namespace Dfe.EarlyYearsQualification.Web.Controllers;
public class QualificationDetailsController(
ILogger<QualificationDetailsController> logger,
IContentService contentService,
IGovUkInsetTextRenderer renderer,
IContentFilterService contentFilterService,
IGovUkInsetTextRenderer govUkInsetTextRenderer,
IHtmlRenderer htmlRenderer,
IUserJourneyCookieService userJourneyCookieService)
: ServiceController
{
Expand All @@ -28,17 +30,15 @@ public async Task<IActionResult> Get()
return RedirectToAction("Index", "Error");
}

var filterParams = userJourneyCookieService.GetUserJourneyModelFromCookie();

var model = MapList(listPageContent, filterParams);
var model = await MapList(listPageContent, await GetFilteredQualifications());

return View(model);
}

[HttpGet("qualification-details/{qualificationId}")]
public async Task<IActionResult> Index(string qualificationId)
{
if (string.IsNullOrEmpty(qualificationId))
if (!ModelState.IsValid || string.IsNullOrEmpty(qualificationId))
{
return BadRequest();
}
Expand All @@ -64,16 +64,90 @@ public async Task<IActionResult> Index(string qualificationId)
return View(model);
}

private static QualificationListModel MapList(QualificationListPage content, UserJourneyModel? filters)
private async Task<List<Qualification>> GetFilteredQualifications()
{
var level = userJourneyCookieService.GetLevelOfQualification();
var (startDateMonth, startDateYear) = userJourneyCookieService.GetWhenWasQualificationAwarded();
var awardingOrganisation = userJourneyCookieService.GetAwardingOrganisation();

return await contentFilterService.GetFilteredQualifications(level, startDateMonth, startDateYear,
awardingOrganisation);
}

private async Task<QualificationListModel> MapList(QualificationListPage content,
List<Qualification>? qualifications)
{
var basicQualificationsModels = GetBasicQualificationsModels(qualifications);

var filterModel = GetFilterModel();

return new QualificationListModel
{
BackButton = content.BackButton,
Filters = filters,
Header = content.Header
Filters = filterModel,
Header = content.Header,
SingleQualificationFoundText = content.SingleQualificationFoundText,
MultipleQualificationsFoundText = content.MultipleQualificationsFoundText,
PreSearchBoxContent = await htmlRenderer.ToHtml(content.PreSearchBoxContent),
SearchButtonText = content.SearchButtonText,
LevelHeading = content.LevelHeading,
AwardingOrganisationHeading = content.AwardingOrganisationHeading,
PostSearchCriteriaContent = await htmlRenderer.ToHtml(content.PostSearchCriteriaContent),
PostQualificationListContent = await htmlRenderer.ToHtml(content.PostQualificationListContent),
SearchCriteriaHeading = content.SearchCriteriaHeading,
Qualifications = basicQualificationsModels.OrderBy(x => x.QualificationName).ToList()
};
}

private FilterModel GetFilterModel()
{
var filterModel = new FilterModel
{
Country = userJourneyCookieService.GetWhereWasQualificationAwarded()!
};

var (startDateMonth, startDateYear) = userJourneyCookieService.GetWhenWasQualificationAwarded();
if (startDateMonth is not null && startDateYear is not null)
{
var date = new DateOnly(startDateYear.Value, startDateMonth.Value, 1);
filterModel.StartDate = $"{date.ToString("MMMM", CultureInfo.InvariantCulture)} {startDateYear.Value}";
}

var level = userJourneyCookieService.GetLevelOfQualification();
if (level is not null && level > 0)
{
filterModel.Level = $"Level {level}";
}

var awardingOrganisation = userJourneyCookieService.GetAwardingOrganisation();
if (!string.IsNullOrEmpty(awardingOrganisation))
{
filterModel.AwardingOrganisation = awardingOrganisation;
}

return filterModel;
}

private static List<BasicQualificationModel> GetBasicQualificationsModels(List<Qualification>? qualifications)
{
var basicQualificationsModels = new List<BasicQualificationModel>();
if (qualifications is not null && qualifications.Count > 0)
{
foreach (var qualification in qualifications)
{
basicQualificationsModels.Add(new BasicQualificationModel
{
QualificationId = qualification.QualificationId,
QualificationLevel = qualification.QualificationLevel,
QualificationName = qualification.QualificationName,
AwardingOrganisationTitle = qualification.AwardingOrganisationTitle
});
}
}

return basicQualificationsModels;
}

private async Task<QualificationDetailsModel> MapDetails(Qualification qualification, DetailsPage content)
{
return new QualificationDetailsModel
Expand All @@ -95,11 +169,11 @@ private async Task<QualificationDetailsModel> MapDetails(Qualification qualifica
BookmarkText = content.BookmarkText,
CheckAnotherQualificationHeading = content.CheckAnotherQualificationHeading,
CheckAnotherQualificationText =
await renderer.ToHtml(content.CheckAnotherQualificationText),
await govUkInsetTextRenderer.ToHtml(content.CheckAnotherQualificationText),
DateAddedLabel = content.DateAddedLabel,
DateOfCheckLabel = content.DateOfCheckLabel,
FurtherInfoHeading = content.FurtherInfoHeading,
FurtherInfoText = await renderer.ToHtml(content.FurtherInfoText),
FurtherInfoText = await govUkInsetTextRenderer.ToHtml(content.FurtherInfoText),
LevelLabel = content.LevelLabel,
MainHeader = content.MainHeader,
QualificationNumberLabel = content.QualificationNumberLabel
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,21 +127,6 @@ public async Task<IActionResult> WhatLevelIsTheQualification(RadioQuestionModel
return RedirectToAction(nameof(this.WhatIsTheAwardingOrganisation));
}

private bool WithinDateRange()
{
var cookie = userJourneyCookieService.GetUserJourneyModelFromCookie();
var qualificationAwardedDateSplit = cookie.WhenWasQualificationAwarded.Split('/');
if (qualificationAwardedDateSplit.Length == 2
&& int.TryParse(qualificationAwardedDateSplit[0], out var parsedStartMonth)
&& int.TryParse(qualificationAwardedDateSplit[1], out var parsedStartYear))
{
var date = new DateOnly(parsedStartYear, parsedStartMonth, 1);
return date >= new DateOnly(2014, 09, 01) && date <= new DateOnly(2019, 08, 31);
}

return false;
}

[HttpGet("what-is-the-awarding-organisation")]
public async Task<IActionResult> WhatIsTheAwardingOrganisation()
{
Expand Down Expand Up @@ -186,28 +171,23 @@ public async Task<IActionResult> WhatIsTheAwardingOrganisation(DropdownQuestionM
return RedirectToAction("Get", "QualificationDetails");
}

private async Task<List<Qualification>> GetFilteredQualifications()
private bool WithinDateRange()
{
var cookie = userJourneyCookieService.GetUserJourneyModelFromCookie();

int? level = null;
if (int.TryParse(cookie.LevelOfQualification, out var parsedLevel))
{
level = parsedLevel;
}

int? startDateMonth = null;
int? startDateYear = null;
var qualificationAwardedDateSplit = cookie.WhenWasQualificationAwarded.Split('/');
if (qualificationAwardedDateSplit.Length == 2
&& int.TryParse(qualificationAwardedDateSplit[0], out var parsedStartMonth)
&& int.TryParse(qualificationAwardedDateSplit[1], out var parsedStartYear))
(int? startDateMonth, int? startDateYear) = userJourneyCookieService.GetWhenWasQualificationAwarded();
if (startDateMonth is not null && startDateYear is not null)
{
startDateMonth = parsedStartMonth;
startDateYear = parsedStartYear;
var date = new DateOnly(startDateYear.Value, startDateMonth.Value, 1);
return date >= new DateOnly(2014, 09, 01) && date <= new DateOnly(2019, 08, 31);
}

return await contentFilterService.GetFilteredQualifications(level, startDateMonth, startDateYear);
return false;
}

private async Task<List<Qualification>> GetFilteredQualifications()
{
int? level = userJourneyCookieService.GetLevelOfQualification();
(int? startDateMonth, int? startDateYear) = userJourneyCookieService.GetWhenWasQualificationAwarded();
return await contentFilterService.GetFilteredQualifications(level, startDateMonth, startDateYear, null);
}

private async Task<IActionResult> GetRadioView(string questionPageId, string actionName, string controllerName)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Dfe.EarlyYearsQualification.Web.Models.Content;

public class BasicQualificationModel
{
public string QualificationId { get; init; } = string.Empty;

public string QualificationName { get; init; } = string.Empty;

public string AwardingOrganisationTitle { get; init; } = string.Empty;

public int QualificationLevel { get; init; }
}
12 changes: 12 additions & 0 deletions src/Dfe.EarlyYearsQualification.Web/Models/Content/FilterModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Dfe.EarlyYearsQualification.Web.Models.Content;

public class FilterModel
{
public string Country { get; init; } = string.Empty;

public string StartDate { get; set; } = string.Empty;

public string Level { get; set; } = "Any level";

public string AwardingOrganisation { get; set; } = "Any organisation";
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,8 @@

namespace Dfe.EarlyYearsQualification.Web.Models.Content;

public class QualificationDetailsModel
public class QualificationDetailsModel : BasicQualificationModel
{
public string QualificationId { get; init; } = string.Empty;
public string QualificationName { get; init; } = string.Empty;
public string AwardingOrganisationTitle { get; init; } = string.Empty;
public int QualificationLevel { get; init; }

public string? FromWhichYear { get; init; }
public string? ToWhichYear { get; init; }
public string? QualificationNumber { get; init; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,27 @@ public class QualificationListModel
{
public string Header { get; init; } = string.Empty;

public UserJourneyModel? Filters { get; init; }
public FilterModel Filters { get; init; } = new();

public NavigationLink? BackButton { get; init; }

public string SingleQualificationFoundText { get; init; } = string.Empty;

public string MultipleQualificationsFoundText { get; init; } = string.Empty;

public string PreSearchBoxContent { get; init; } = string.Empty;

public string SearchButtonText { get; init; } = string.Empty;

public string LevelHeading { get; init; } = string.Empty;

public string AwardingOrganisationHeading { get; init; } = string.Empty;

public string PostQualificationListContent { get; init; } = string.Empty;

public string SearchCriteriaHeading { get; init; } = string.Empty;

public string PostSearchCriteriaContent { get; init; } = string.Empty;

public List<BasicQualificationModel> Qualifications { get; init; } = [];
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,9 @@ public interface IUserJourneyCookieService
public void SetAwardingOrganisation(string awardingOrganisation);
public UserJourneyModel GetUserJourneyModelFromCookie();
public void ResetUserJourneyCookie();

public string? GetWhereWasQualificationAwarded();
public (int? startMonth, int? startYear) GetWhenWasQualificationAwarded();
public int? GetLevelOfQualification();
public string? GetAwardingOrganisation();
}
Loading

0 comments on commit 41933b0

Please sign in to comment.