From c358cfb03f6d147c967f7bc44aa464e4dd6f1ef6 Mon Sep 17 00:00:00 2001 From: killij <51908793+killij@users.noreply.github.com> Date: Tue, 24 Dec 2024 14:24:09 +0000 Subject: [PATCH 1/3] FAI-2175 - Create the new "Add a location" page --- .../Employer.Web/Configuration/IoC.cs | 2 +- .../Part1/EnterLocationManuallyController.cs | 2 +- .../Controllers/Part1/LocationController.cs | 113 +++++++++-- .../Part1/MultipleLocationsController.cs | 76 ++++++-- .../Part1/RecruitNationallyController.cs | 53 +++--- .../Part1/SelectLocationController.cs | 2 +- .../Part1/MultipleLocationsOrchestrator.cs | 99 ---------- .../Services/VacancyLocationService.cs | 59 ++++++ src/Employer/Employer.Web/Utility.cs | 2 + .../Part1/Location/AddOneLocationEditModel.cs | 8 + .../Part1/Location/AddOneLocationViewModel.cs | 13 ++ .../Part1/Location/AddOneLocation.cshtml | 61 ++++++ .../Part1/Location/_AddressesView.cshtml | 22 +++ .../Location/_GroupedAddressesView.cshtml | 28 +++ .../AddMoreThanOneLocation.cshtml | 11 +- .../MultipleLocations/_AddressesView.cshtml | 45 ++--- .../_GroupedAddressesView.cshtml | 55 +++--- .../Part1/LocationControllerTests.cs | 175 ++++++++++++++++++ .../Part1/MultipleLocationsControllerTests.cs | 106 +++++++---- .../Part1/RecruitNationallyControllerTests.cs | 56 +++--- .../Fluent/FluentVacancyValidator.cs | 26 ++- 21 files changed, 707 insertions(+), 307 deletions(-) delete mode 100644 src/Employer/Employer.Web/Orchestrators/Part1/MultipleLocationsOrchestrator.cs create mode 100644 src/Employer/Employer.Web/Services/VacancyLocationService.cs create mode 100644 src/Employer/Employer.Web/ViewModels/Part1/Location/AddOneLocationEditModel.cs create mode 100644 src/Employer/Employer.Web/ViewModels/Part1/Location/AddOneLocationViewModel.cs create mode 100644 src/Employer/Employer.Web/Views/Part1/Location/AddOneLocation.cshtml create mode 100644 src/Employer/Employer.Web/Views/Part1/Location/_AddressesView.cshtml create mode 100644 src/Employer/Employer.Web/Views/Part1/Location/_GroupedAddressesView.cshtml create mode 100644 src/Employer/UnitTests/Employer.Web/Controllers/Part1/LocationControllerTests.cs diff --git a/src/Employer/Employer.Web/Configuration/IoC.cs b/src/Employer/Employer.Web/Configuration/IoC.cs index e367b41ad8..3c3b19d32d 100644 --- a/src/Employer/Employer.Web/Configuration/IoC.cs +++ b/src/Employer/Employer.Web/Configuration/IoC.cs @@ -79,6 +79,7 @@ private static void RegisterServiceDeps(IServiceCollection services, IConfigurat services.AddTransient(); services.AddTransient(); services.AddSingleton(); + services.AddSingleton(); } private static void RegisterFluentValidators(IServiceCollection services) @@ -140,7 +141,6 @@ private static void RegisterOrchestratorDeps(IServiceCollection services) services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient(); } private static void RegisterMapperDeps(IServiceCollection services) diff --git a/src/Employer/Employer.Web/Controllers/Part1/EnterLocationManuallyController.cs b/src/Employer/Employer.Web/Controllers/Part1/EnterLocationManuallyController.cs index 20f7730325..685de83046 100644 --- a/src/Employer/Employer.Web/Controllers/Part1/EnterLocationManuallyController.cs +++ b/src/Employer/Employer.Web/Controllers/Part1/EnterLocationManuallyController.cs @@ -53,7 +53,7 @@ public async Task EnterLocationManually( TempData[TempDataKeys.AddedLocation] = newAddress.ToAddressString(); TempData.Remove(TempDataKeys.Postcode); return model.Origin == MultipleLocationsJourneyOrigin.One - ? NotFound() + ? RedirectToRoute(RouteNames.AddOneLocation_Get, new { model.VacancyId, model.EmployerAccountId, model.Wizard }) : RedirectToRoute(RouteNames.AddMoreThanOneLocation_Get, new { model.VacancyId, model.EmployerAccountId, model.Wizard }); } diff --git a/src/Employer/Employer.Web/Controllers/Part1/LocationController.cs b/src/Employer/Employer.Web/Controllers/Part1/LocationController.cs index e3127d2547..d31e1d20fe 100644 --- a/src/Employer/Employer.Web/Controllers/Part1/LocationController.cs +++ b/src/Employer/Employer.Web/Controllers/Part1/LocationController.cs @@ -1,36 +1,33 @@ +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Esfa.Recruit.Employer.Web.Configuration; using Esfa.Recruit.Employer.Web.Configuration.Routing; using Esfa.Recruit.Employer.Web.Extensions; using Esfa.Recruit.Employer.Web.Orchestrators.Part1; using Esfa.Recruit.Employer.Web.RouteModel; +using Esfa.Recruit.Employer.Web.Services; using Esfa.Recruit.Employer.Web.ViewModels.Part1.Location; +using Esfa.Recruit.Shared.Web; using Esfa.Recruit.Shared.Web.Extensions; -using Esfa.Recruit.Vacancies.Client.Application.FeatureToggle; +using Esfa.Recruit.Vacancies.Client.Domain.Entities; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; +using Microsoft.FeatureManagement.Mvc; namespace Esfa.Recruit.Employer.Web.Controllers.Part1 { [Route(RoutePaths.AccountVacancyRoutePath)] - public class LocationController : EmployerControllerBase + public class LocationController(IWebHostEnvironment hostingEnvironment) : EmployerControllerBase(hostingEnvironment) { - private readonly LocationOrchestrator _orchestrator; - private readonly IFeature _feature; - - public LocationController(LocationOrchestrator orchestrator, IWebHostEnvironment hostingEnvironment, IFeature feature) - : base(hostingEnvironment) - { - _orchestrator = orchestrator; - _feature = feature; - } - + #region When FeatureNames.MultipleLocations feature flag is removed, all this can be removed + [HttpGet("location", Name = RouteNames.Location_Get)] - public async Task Location(VacancyRouteModel vrm, [FromQuery] string wizard = "true") + public async Task Location([FromServices] LocationOrchestrator orchestrator, VacancyRouteModel vrm, [FromQuery] string wizard = "true") { var employerInfoModel = GetVacancyEmployerInfoCookie(vrm.VacancyId); - var vm = await _orchestrator.GetLocationViewModelAsync(vrm, employerInfoModel, User.ToVacancyUser()); + var vm = await orchestrator.GetLocationViewModelAsync(vrm, employerInfoModel, User.ToVacancyUser()); vm.PageInfo.SetWizard(wizard); @@ -42,17 +39,17 @@ public async Task Location(VacancyRouteModel vrm, [FromQuery] str //either part 1 is not completed or part 1 is completed but part 2 has not started if (employerInfoModel == null && (!vm.PageInfo.HasCompletedPartOne || !vm.PageInfo.HasStartedPartTwo)) { - employerInfoModel = await _orchestrator.GetVacancyEmployerInfoModelAsync(vrm); + employerInfoModel = await orchestrator.GetVacancyEmployerInfoModelAsync(vrm); SetVacancyEmployerInfoCookie(employerInfoModel); } return View(vm); } [HttpPost("location", Name = RouteNames.Location_Post)] - public async Task Location(LocationEditModel model, [FromQuery] bool wizard) + public async Task Location([FromServices] LocationOrchestrator orchestrator, LocationEditModel model, [FromQuery] bool wizard) { var employerInfoModel = GetVacancyEmployerInfoCookie(model.VacancyId); - var response = await _orchestrator.PostLocationEditModelAsync(model, employerInfoModel, User.ToVacancyUser()); + var response = await orchestrator.PostLocationEditModelAsync(model, employerInfoModel, User.ToVacancyUser()); if (!response.Success) { @@ -61,7 +58,7 @@ public async Task Location(LocationEditModel model, [FromQuery] b if (!ModelState.IsValid) { - var vm = await _orchestrator.GetLocationViewModelAsync(model, employerInfoModel, User.ToVacancyUser()); + var vm = await orchestrator.GetLocationViewModelAsync(model, employerInfoModel, User.ToVacancyUser()); vm.SelectedLocation = model.SelectedLocation; vm.PageInfo.SetWizard(wizard); vm.CanShowBackLink = employerInfoModel != null || vm.PageInfo.IsWizard; @@ -87,10 +84,86 @@ public IActionResult Cancel(VacancyRouteModel vrm, [FromQuery] bool wizard) } [HttpGet("location/GetAddresses")] - public async Task GetAddresses([FromQuery] string searchTerm) + public async Task GetAddresses([FromServices] LocationOrchestrator orchestrator, [FromQuery] string searchTerm) { - var result = await _orchestrator.GetAddresses(searchTerm); + var result = await orchestrator.GetAddresses(searchTerm); return Ok(result); } + + #endregion + + [FeatureGate(FeatureNames.MultipleLocations)] + [HttpGet("add-one-location", Name = RouteNames.AddOneLocation_Get)] + public async Task AddOneLocation( + [FromServices] IVacancyLocationService vacancyLocationService, + [FromServices] IUtility utility, + VacancyRouteModel vacancyRouteModel, + [FromQuery] bool wizard) + { + var vacancy = await utility.GetAuthorisedVacancyForEditAsync(vacancyRouteModel, RouteNames.AddOneLocation_Get); + var allLocations = await vacancyLocationService.GetVacancyLocations(vacancy); + var selectedLocation = vacancy.EmployerLocations is { Count: 1 } ? vacancy.EmployerLocations.First() : null; + + var viewModel = new AddOneLocationViewModel + { + ApprenticeshipTitle = vacancy.Title, + AvailableLocations = allLocations ?? [], + VacancyId = vacancyRouteModel.VacancyId, + EmployerAccountId = vacancyRouteModel.EmployerAccountId, + PageInfo = utility.GetPartOnePageInfo(vacancy), + SelectedLocation = selectedLocation?.ToAddressString(), + }; + viewModel.PageInfo.SetWizard(wizard); + + if (TempData[TempDataKeys.AddedLocation] is string newlyAddedLocation) + { + viewModel.SelectedLocation = newlyAddedLocation; + viewModel.BannerAddress = newlyAddedLocation; + } + return View(viewModel); + } + + private static readonly Dictionary ValidationFieldMappings = new() + { + { "EmployerLocations", "SelectedLocation" } + }; + + [FeatureGate(FeatureNames.MultipleLocations)] + [HttpPost("add-one-location", Name = RouteNames.AddOneLocation_Post)] + public async Task AddOneLocation( + [FromServices] IVacancyLocationService vacancyLocationService, + [FromServices] IUtility utility, + AddOneLocationEditModel model, + [FromQuery] bool wizard) + { + var vacancy = await utility.GetAuthorisedVacancyForEditAsync(model, RouteNames.AddOneLocation_Post); + var allLocations = await vacancyLocationService.GetVacancyLocations(vacancy); + var location = allLocations.FirstOrDefault(x => x.ToAddressString() == model.SelectedLocation); + var result = await vacancyLocationService.UpdateDraftVacancyLocations( + vacancy, + User.ToVacancyUser(), + AvailableWhere.OneLocation, + location is null ? null : [location]); + + if (result.ValidationResult is null) + { + return wizard + ? RedirectToRoute(RouteNames.EmployerTaskListGet, new { model.VacancyId, model.EmployerAccountId, wizard }) + : RedirectToRoute(RouteNames.EmployerCheckYourAnswersGet, new { model.VacancyId, model.EmployerAccountId }); + } + + ModelState.AddValidationErrors(result.ValidationResult, ValidationFieldMappings); + var viewModel = new AddOneLocationViewModel + { + ApprenticeshipTitle = vacancy.Title, + AvailableLocations = allLocations ?? [], + VacancyId = model.VacancyId, + EmployerAccountId = model.EmployerAccountId, + PageInfo = utility.GetPartOnePageInfo(vacancy), + SelectedLocation = model.SelectedLocation + }; + viewModel.PageInfo.SetWizard(wizard); + return View(viewModel); + } } } \ No newline at end of file diff --git a/src/Employer/Employer.Web/Controllers/Part1/MultipleLocationsController.cs b/src/Employer/Employer.Web/Controllers/Part1/MultipleLocationsController.cs index 0aa6c35a42..20818caed7 100644 --- a/src/Employer/Employer.Web/Controllers/Part1/MultipleLocationsController.cs +++ b/src/Employer/Employer.Web/Controllers/Part1/MultipleLocationsController.cs @@ -1,18 +1,18 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text.Json; using System.Threading.Tasks; using Esfa.Recruit.Employer.Web.Configuration; using Esfa.Recruit.Employer.Web.Configuration.Routing; using Esfa.Recruit.Employer.Web.Extensions; -using Esfa.Recruit.Employer.Web.Orchestrators.Part1; using Esfa.Recruit.Employer.Web.RouteModel; +using Esfa.Recruit.Employer.Web.Services; using Esfa.Recruit.Employer.Web.ViewModels.Part1.MultipleLocations; +using Esfa.Recruit.Shared.Web; using Esfa.Recruit.Shared.Web.Domain; using Esfa.Recruit.Shared.Web.Extensions; -using Esfa.Recruit.Vacancies.Client.Application.Validation; using Esfa.Recruit.Vacancies.Client.Domain.Entities; -using Esfa.Recruit.Vacancies.Client.Infrastructure.Client; using Microsoft.AspNetCore.Mvc; using Microsoft.FeatureManagement.Mvc; @@ -43,7 +43,7 @@ public async Task LocationAvailability( { return model.SelectedAvailability switch { - AvailableWhere.OneLocation => throw new NotImplementedException(), + AvailableWhere.OneLocation => RedirectToRoute(RouteNames.AddOneLocation_Get, new { model.VacancyId, model.EmployerAccountId, wizard }), AvailableWhere.MultipleLocations => RedirectToRoute(RouteNames.AddMoreThanOneLocation_Get, new { model.VacancyId, model.EmployerAccountId, wizard }), AvailableWhere.AcrossEngland => RedirectToRoute(RouteNames.RecruitNationally_Get, new { model.VacancyId, model.EmployerAccountId, wizard }), _ => throw new NotImplementedException(), @@ -73,18 +73,32 @@ private static async Task GetLocationAvailability [FeatureGate(FeatureNames.MultipleLocations)] [HttpGet("add-many-locations", Name = RouteNames.AddMoreThanOneLocation_Get)] public async Task AddMoreThanOneLocation( - [FromServices] IMultipleLocationsOrchestrator orchestrator, - VacancyRouteModel vacancyRouteModel, + [FromServices] IVacancyLocationService vacancyLocationService, + [FromServices] IUtility utility, + VacancyRouteModel model, [FromQuery] bool wizard) { - var viewModel = await orchestrator.GetAddMoreThanOneLocationViewModelAsync(vacancyRouteModel, wizard); + var vacancy = await utility.GetAuthorisedVacancyForEditAsync(model, RouteNames.AddMoreThanOneLocation_Get); + var allLocations = await vacancyLocationService.GetVacancyLocations(vacancy); - // Override with stored values after a round trip through add new location - if (TempData[TempDataKeys.SelectedLocations] is string value) + var selectedLocations = vacancy.EmployerLocations switch { - viewModel.SelectedLocations = JsonSerializer.Deserialize>(value); - } - + _ when TempData[TempDataKeys.SelectedLocations] is string value => JsonSerializer.Deserialize>(value), + { Count: >0 } => vacancy.EmployerLocations.Select(l => l.ToAddressString()).ToList(), + _ => [] + }; + + var viewModel = new AddMoreThanOneLocationViewModel + { + ApprenticeshipTitle = vacancy.Title, + AvailableLocations = allLocations ?? [], + VacancyId = model.VacancyId, + EmployerAccountId = model.EmployerAccountId, + PageInfo = utility.GetPartOnePageInfo(vacancy), + SelectedLocations = selectedLocations + }; + viewModel.PageInfo.SetWizard(wizard); + if (TempData[TempDataKeys.AddedLocation] is string newlyAddedLocation) { viewModel.SelectedLocations.Add(newlyAddedLocation); @@ -94,28 +108,52 @@ public async Task AddMoreThanOneLocation( return View(viewModel); } + private static readonly Dictionary ValidationFieldMappings = new() + { + { "EmployerLocations", "SelectedLocations" } + }; + [FeatureGate(FeatureNames.MultipleLocations)] [HttpPost("add-many-locations", Name = RouteNames.AddMoreThanOneLocation_Post)] public async Task AddMoreThanOneLocation( - [FromServices] IMultipleLocationsOrchestrator orchestrator, + [FromServices] IVacancyLocationService vacancyLocationService, + [FromServices] IUtility utility, AddMoreThanOneLocationEditModel editModel, [FromQuery] bool wizard) { - var result = await orchestrator.PostAddMoreThanOneLocationViewModelAsync(editModel, User.ToVacancyUser()); + var vacancy = await utility.GetAuthorisedVacancyForEditAsync(editModel, RouteNames.AddMoreThanOneLocation_Post); + var allLocations = await vacancyLocationService.GetVacancyLocations(vacancy); + var locations = editModel.SelectedLocations + .Select(x => allLocations.FirstOrDefault(l => l.ToAddressString() == x)) + .Where(x => x is not null) + .ToList(); + var result = await vacancyLocationService.UpdateDraftVacancyLocations( + vacancy, + User.ToVacancyUser(), + AvailableWhere.MultipleLocations, + locations); - if (result.Success) + if (result.ValidationResult is null) { return RedirectToRoute(RouteNames.MultipleLocationsConfirm_Get, new { editModel.VacancyId, editModel.EmployerAccountId, wizard } ); } + + ModelState.AddValidationErrors(result.ValidationResult, ValidationFieldMappings); + var viewModel = new AddMoreThanOneLocationViewModel + { + ApprenticeshipTitle = vacancy.Title, + AvailableLocations = allLocations ?? [], + VacancyId = editModel.VacancyId, + EmployerAccountId = editModel.EmployerAccountId, + PageInfo = utility.GetPartOnePageInfo(vacancy), + SelectedLocations = editModel.SelectedLocations + }; - result.AddErrorsToModelState(ModelState); - var viewModel = await orchestrator.GetAddMoreThanOneLocationViewModelAsync(editModel, wizard); - viewModel.SelectedLocations = editModel.SelectedLocations; return View(viewModel); } [FeatureGate(FeatureNames.MultipleLocations)] - [HttpPost("addnewlocation", Name = RouteNames.AddNewLocationJourney_Post)] + [HttpPost("add-many-locations/add-new-location", Name = RouteNames.AddNewLocationJourney_Post)] public IActionResult AddALocation(AddMoreThanOneLocationEditModel editModel, [FromQuery] bool wizard) { TempData.Remove(TempDataKeys.Postcode); diff --git a/src/Employer/Employer.Web/Controllers/Part1/RecruitNationallyController.cs b/src/Employer/Employer.Web/Controllers/Part1/RecruitNationallyController.cs index 8636716049..a4d189582d 100644 --- a/src/Employer/Employer.Web/Controllers/Part1/RecruitNationallyController.cs +++ b/src/Employer/Employer.Web/Controllers/Part1/RecruitNationallyController.cs @@ -5,11 +5,10 @@ using Esfa.Recruit.Employer.Web.Extensions; using Esfa.Recruit.Employer.Web.Models.AddLocation; using Esfa.Recruit.Employer.Web.RouteModel; +using Esfa.Recruit.Employer.Web.Services; using Esfa.Recruit.Employer.Web.ViewModels.Part1.RecruitNationally; using Esfa.Recruit.Shared.Web; -using Esfa.Recruit.Vacancies.Client.Application.Validation; using Esfa.Recruit.Vacancies.Client.Domain.Entities; -using Esfa.Recruit.Vacancies.Client.Infrastructure.Client; using Microsoft.AspNetCore.Mvc; using Microsoft.FeatureManagement.Mvc; @@ -28,51 +27,53 @@ public class RecruitNationallyController: Controller public async Task RecruitNationally([FromServices] IUtility utility, VacancyRouteModel model, [FromQuery] bool wizard = true) { var vacancy = await utility.GetAuthorisedVacancyForEditAsync(model, RouteNames.RecruitNationally_Get); - var viewModel = await GetViewModel(utility, model, RouteNames.RecruitNationally_Get, vacancy.EmployerLocationInformation, wizard); + var viewModel = new RecruitNationallyViewModel + { + ApprenticeshipTitle = vacancy.Title, + EmployerAccountId = model.EmployerAccountId, + AdditionalInformation = vacancy.EmployerLocationInformation, + PageInfo = utility.GetPartOnePageInfo(vacancy), + VacancyId = model.VacancyId, + }; + viewModel.PageInfo.SetWizard(wizard); + return View(viewModel); } [FeatureGate(FeatureNames.MultipleLocations)] [HttpPost("location-information", Name = RouteNames.RecruitNationally_Post)] public async Task RecruitNationally( - [FromServices] IRecruitVacancyClient recruitVacancyClient, + [FromServices] IVacancyLocationService vacancyLocationService, [FromServices] IUtility utility, RecruitNationallyEditModel model, [FromQuery] bool wizard = true) { var vacancy = await utility.GetAuthorisedVacancyForEditAsync(model, RouteNames.AddMoreThanOneLocation_Post); - vacancy.EmployerLocationOption = AvailableWhere.AcrossEngland; - vacancy.EmployerLocationInformation = model.AdditionalInformation; - vacancy.EmployerLocation = null; - vacancy.EmployerLocations = null; + var result = await vacancyLocationService.UpdateDraftVacancyLocations( + vacancy, + User.ToVacancyUser(), + AvailableWhere.AcrossEngland, + null, + model.AdditionalInformation); - var validationResult = recruitVacancyClient.Validate(vacancy, VacancyRuleSet.EmployerAddress); - if (validationResult.HasErrors) + if (result.ValidationResult is null) { - ModelState.AddValidationErrors(validationResult, ValidationFieldMappings); - var viewModel = await GetViewModel(utility, model, RouteNames.RecruitNationally_Post, model.AdditionalInformation, wizard); - return View(viewModel); + return wizard + ? RedirectToRoute(RouteNames.EmployerTaskListGet, new {model.VacancyId, model.EmployerAccountId, wizard}) + : RedirectToRoute(RouteNames.EmployerCheckYourAnswersGet, new {model.VacancyId, model.EmployerAccountId}); } - - await recruitVacancyClient.UpdateDraftVacancyAsync(vacancy, User.ToVacancyUser()); - return wizard - ? RedirectToRoute(RouteNames.EmployerTaskListGet, new {model.VacancyId, model.EmployerAccountId, wizard}) - : RedirectToRoute(RouteNames.EmployerCheckYourAnswersGet, new {model.VacancyId, model.EmployerAccountId}); - } - - private static async Task GetViewModel(IUtility utility, VacancyRouteModel model, string routeName, string locationAdditionalInfo, bool wizard) - { - var vacancy = await utility.GetAuthorisedVacancyForEditAsync(model, routeName); + + ModelState.AddValidationErrors(result.ValidationResult, ValidationFieldMappings); var viewModel = new RecruitNationallyViewModel { ApprenticeshipTitle = vacancy.Title, EmployerAccountId = model.EmployerAccountId, - AdditionalInformation = locationAdditionalInfo, + AdditionalInformation = model.AdditionalInformation, PageInfo = utility.GetPartOnePageInfo(vacancy), VacancyId = model.VacancyId, }; viewModel.PageInfo.SetWizard(wizard); - - return viewModel; + + return View(viewModel); } } \ No newline at end of file diff --git a/src/Employer/Employer.Web/Controllers/Part1/SelectLocationController.cs b/src/Employer/Employer.Web/Controllers/Part1/SelectLocationController.cs index 682a7a228e..1a8993a474 100644 --- a/src/Employer/Employer.Web/Controllers/Part1/SelectLocationController.cs +++ b/src/Employer/Employer.Web/Controllers/Part1/SelectLocationController.cs @@ -54,7 +54,7 @@ public async Task SelectLocation([FromServices] IRecruitVacancyCl TempData[TempDataKeys.AddedLocation] = addressToAdd.ToAddressString(); TempData.Remove(TempDataKeys.Postcode); return model.Origin == MultipleLocationsJourneyOrigin.One - ? NotFound() + ? RedirectToRoute(RouteNames.AddOneLocation_Get, new { model.VacancyId, model.EmployerAccountId, model.Wizard }) : RedirectToRoute(RouteNames.AddMoreThanOneLocation_Get, new { model.VacancyId, model.EmployerAccountId, model.Wizard }); } diff --git a/src/Employer/Employer.Web/Orchestrators/Part1/MultipleLocationsOrchestrator.cs b/src/Employer/Employer.Web/Orchestrators/Part1/MultipleLocationsOrchestrator.cs deleted file mode 100644 index 18518196e1..0000000000 --- a/src/Employer/Employer.Web/Orchestrators/Part1/MultipleLocationsOrchestrator.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Esfa.Recruit.Employer.Web.Configuration.Routing; -using Esfa.Recruit.Employer.Web.RouteModel; -using Esfa.Recruit.Employer.Web.ViewModels.Part1.MultipleLocations; -using Esfa.Recruit.Shared.Web.Extensions; -using Esfa.Recruit.Shared.Web.Orchestrators; -using Esfa.Recruit.Vacancies.Client.Application.Validation; -using Esfa.Recruit.Vacancies.Client.Domain.Entities; -using Esfa.Recruit.Vacancies.Client.Infrastructure.Client; -using Microsoft.Extensions.Logging; -using AvailableWhere = Esfa.Recruit.Vacancies.Client.Domain.Entities.AvailableWhere; - -namespace Esfa.Recruit.Employer.Web.Orchestrators.Part1; - -public interface IMultipleLocationsOrchestrator -{ - Task GetAddMoreThanOneLocationViewModelAsync(VacancyRouteModel vrm, bool wizard); - Task PostAddMoreThanOneLocationViewModelAsync(AddMoreThanOneLocationEditModel editModel, VacancyUser user); -} - -public class MultipleLocationsOrchestrator( - IEmployerVacancyClient employerVacancyClient, - ILogger logger, - IRecruitVacancyClient recruitVacancyClient, - IUtility utility - ) : VacancyValidatingOrchestrator(logger), IMultipleLocationsOrchestrator -{ - private const VacancyRuleSet ValidationRules = VacancyRuleSet.EmployerAddress; - - protected override EntityToViewModelPropertyMappings DefineMappings() - { - return new EntityToViewModelPropertyMappings - { - { e => e.EmployerLocations, m => m.SelectedLocations } - }; - } - - public async Task GetAddMoreThanOneLocationViewModelAsync( - VacancyRouteModel vrm, - bool wizard) - { - var vacancy = await utility.GetAuthorisedVacancyForEditAsync(vrm, RouteNames.AddMoreThanOneLocation_Get); - var employerProfile = await recruitVacancyClient.GetEmployerProfileAsync(vacancy.EmployerAccountId, vacancy.AccountLegalEntityPublicHashedId); - var allLocations = await GetAllAvailableLocationsAsync(employerProfile); - - var viewModel = new AddMoreThanOneLocationViewModel - { - ApprenticeshipTitle = vacancy.Title, - AvailableLocations = allLocations ?? [], - VacancyId = vrm.VacancyId, - EmployerAccountId = vrm.EmployerAccountId, - PageInfo = utility.GetPartOnePageInfo(vacancy), - SelectedLocations = vacancy.EmployerLocations?.Select(l => l.ToAddressString()).ToList() ?? [], - }; - viewModel.PageInfo.SetWizard(wizard); - return viewModel; - } - - private async Task> GetAllAvailableLocationsAsync(EmployerProfile employerProfile) - { - var employerData = await employerVacancyClient.GetEditVacancyInfoAsync(employerProfile.EmployerAccountId); - var legalEntity = employerData.LegalEntities.First(l => l.AccountLegalEntityPublicHashedId == employerProfile.AccountLegalEntityPublicHashedId); - var locations = new List
(); - - var legalAddress = legalEntity.Address.ConvertToDomainAddress(); - if (legalAddress.IsEmpty() is false) - { - locations.Add(legalAddress); - }; - locations.AddRange(employerProfile.OtherLocations.Where(x => !x.IsEmpty())); - return locations; - } - - public async Task PostAddMoreThanOneLocationViewModelAsync(AddMoreThanOneLocationEditModel editModel, VacancyUser user) - { - var vacancy = await utility.GetAuthorisedVacancyForEditAsync(editModel, RouteNames.AddMoreThanOneLocation_Post); - var employerProfile = await recruitVacancyClient.GetEmployerProfileAsync(vacancy.EmployerAccountId, vacancy.AccountLegalEntityPublicHashedId); - var allLocations = await GetAllAvailableLocationsAsync(employerProfile); - - vacancy.EmployerLocationOption = AvailableWhere.MultipleLocations; - vacancy.EmployerLocation = null; - vacancy.EmployerLocationInformation = null; - // This maintains the order in which the user viewed the addresses when selected - vacancy.EmployerLocations = editModel.SelectedLocations - .Select(x => allLocations.FirstOrDefault(l => l.ToAddressString() == x)) - .Where(x => x is not null) - .ToList(); - - return await ValidateAndExecute( - vacancy, - v => recruitVacancyClient.Validate(v, ValidationRules), - async v => - { - await recruitVacancyClient.UpdateDraftVacancyAsync(vacancy, user); - }); - } -} \ No newline at end of file diff --git a/src/Employer/Employer.Web/Services/VacancyLocationService.cs b/src/Employer/Employer.Web/Services/VacancyLocationService.cs new file mode 100644 index 0000000000..52b0d0e999 --- /dev/null +++ b/src/Employer/Employer.Web/Services/VacancyLocationService.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Esfa.Recruit.Shared.Web.Extensions; +using Esfa.Recruit.Vacancies.Client.Application.Validation; +using Esfa.Recruit.Vacancies.Client.Domain.Entities; +using Esfa.Recruit.Vacancies.Client.Infrastructure.Client; + +namespace Esfa.Recruit.Employer.Web.Services; + +public interface IVacancyLocationService +{ + public Task> GetVacancyLocations(Vacancy vacancy); + public Task UpdateDraftVacancyLocations(Vacancy vacancy, VacancyUser user, AvailableWhere availableWhere, List
locations, string locationInformation = null); +} + +public record UpdateVacancyLocationsResult(EntityValidationResult ValidationResult); + +public class VacancyLocationService(IRecruitVacancyClient recruitVacancyClient, IEmployerVacancyClient employerVacancyClient): IVacancyLocationService +{ + public async Task> GetVacancyLocations(Vacancy vacancy) + { + ArgumentNullException.ThrowIfNull(vacancy); + var employerProfile = await recruitVacancyClient.GetEmployerProfileAsync(vacancy.EmployerAccountId, vacancy.AccountLegalEntityPublicHashedId); + + var employerData = await employerVacancyClient.GetEditVacancyInfoAsync(employerProfile.EmployerAccountId); + var legalEntity = employerData.LegalEntities.First(l => l.AccountLegalEntityPublicHashedId == employerProfile.AccountLegalEntityPublicHashedId); + var locations = new List
(); + + var legalAddress = legalEntity.Address.ConvertToDomainAddress(); + if (legalAddress.IsEmpty() is false) + { + locations.Add(legalAddress); + }; + locations.AddRange(employerProfile.OtherLocations.Where(x => !x.IsEmpty())); + return locations; + } + + public async Task UpdateDraftVacancyLocations(Vacancy vacancy, VacancyUser user, AvailableWhere availableWhere, List
locations = null, string locationInformation = null) + { + ArgumentNullException.ThrowIfNull(vacancy); + ArgumentNullException.ThrowIfNull(user); + + vacancy.EmployerLocation = null; // null it for records created before this feature that are edited + vacancy.EmployerLocationOption = availableWhere; + vacancy.EmployerLocations = locations; + vacancy.EmployerLocationInformation = locationInformation; + + var validationResult = recruitVacancyClient.Validate(vacancy, VacancyRuleSet.EmployerAddress); + if (validationResult.HasErrors) + { + return new UpdateVacancyLocationsResult(validationResult); + } + + await recruitVacancyClient.UpdateDraftVacancyAsync(vacancy, user); + return new UpdateVacancyLocationsResult(null); + } +} \ No newline at end of file diff --git a/src/Employer/Employer.Web/Utility.cs b/src/Employer/Employer.Web/Utility.cs index 2d10bc84ef..4517da3b10 100644 --- a/src/Employer/Employer.Web/Utility.cs +++ b/src/Employer/Employer.Web/Utility.cs @@ -214,6 +214,8 @@ public IList GetPermittedRoutesForVacancy(Vacancy vacancy) RouteNames.MultipleLocations_Post, RouteNames.AddMoreThanOneLocation_Get, RouteNames.AddMoreThanOneLocation_Post, + RouteNames.AddOneLocation_Get, + RouteNames.AddOneLocation_Post, RouteNames.AddLocation_Get, RouteNames.AddLocation_Post, RouteNames.SelectAnAddress_Get, diff --git a/src/Employer/Employer.Web/ViewModels/Part1/Location/AddOneLocationEditModel.cs b/src/Employer/Employer.Web/ViewModels/Part1/Location/AddOneLocationEditModel.cs new file mode 100644 index 0000000000..4bb59e0f9d --- /dev/null +++ b/src/Employer/Employer.Web/ViewModels/Part1/Location/AddOneLocationEditModel.cs @@ -0,0 +1,8 @@ +using Esfa.Recruit.Employer.Web.RouteModel; + +namespace Esfa.Recruit.Employer.Web.ViewModels.Part1.Location; + +public class AddOneLocationEditModel: VacancyRouteModel +{ + public string SelectedLocation { get; set; } +} \ No newline at end of file diff --git a/src/Employer/Employer.Web/ViewModels/Part1/Location/AddOneLocationViewModel.cs b/src/Employer/Employer.Web/ViewModels/Part1/Location/AddOneLocationViewModel.cs new file mode 100644 index 0000000000..47c2da3a31 --- /dev/null +++ b/src/Employer/Employer.Web/ViewModels/Part1/Location/AddOneLocationViewModel.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using Esfa.Recruit.Shared.Web.ViewModels; +using Esfa.Recruit.Vacancies.Client.Domain.Entities; + +namespace Esfa.Recruit.Employer.Web.ViewModels.Part1.Location; + +public class AddOneLocationViewModel: AddOneLocationEditModel +{ + public string ApprenticeshipTitle { get; init; } + public List
AvailableLocations { get; set; } = []; + public string BannerAddress { get; set; } + public PartOnePageInfoViewModel PageInfo { get; init; } +} \ No newline at end of file diff --git a/src/Employer/Employer.Web/Views/Part1/Location/AddOneLocation.cshtml b/src/Employer/Employer.Web/Views/Part1/Location/AddOneLocation.cshtml new file mode 100644 index 0000000000..8cc4f428ec --- /dev/null +++ b/src/Employer/Employer.Web/Views/Part1/Location/AddOneLocation.cshtml @@ -0,0 +1,61 @@ +@using Esfa.Recruit.Shared.Web.Domain +@using Esfa.Recruit.Shared.Web.TagHelpers + +@model Esfa.Recruit.Employer.Web.ViewModels.Part1.Location.AddOneLocationViewModel + +@{ + ViewBag.Vpv = "/recruitment/employer/page-part1-location"; + ViewBag.Title = "Add a location"; + ViewBag.ZenDeskLabel = "recruit-where-will-the-apprentice-work"; +} + +@section BackLink { + Back + Back +} + +
+
+ + +

+ @Model.BannerAddress added. +

+
+ + +
+ +
+
+ +

+ @Model.ApprenticeshipTitle + Add a location +

+
+

+ Select one location +

+
+ +
+ + +
+
+
+
+ + + +
+ + Cancel + Cancel +
+
+
+
\ No newline at end of file diff --git a/src/Employer/Employer.Web/Views/Part1/Location/_AddressesView.cshtml b/src/Employer/Employer.Web/Views/Part1/Location/_AddressesView.cshtml new file mode 100644 index 0000000000..60550a6608 --- /dev/null +++ b/src/Employer/Employer.Web/Views/Part1/Location/_AddressesView.cshtml @@ -0,0 +1,22 @@ +@model Esfa.Recruit.Employer.Web.ViewModels.Part1.Location.AddOneLocationViewModel + +@{ + var index = 0; + foreach (var location in Model.AvailableLocations) + { + var address = location.ToAddressString(); + var controlId = $"selectedLocation_{index++}"; +
+ +
+ } +} \ No newline at end of file diff --git a/src/Employer/Employer.Web/Views/Part1/Location/_GroupedAddressesView.cshtml b/src/Employer/Employer.Web/Views/Part1/Location/_GroupedAddressesView.cshtml new file mode 100644 index 0000000000..0d08b3c7a6 --- /dev/null +++ b/src/Employer/Employer.Web/Views/Part1/Location/_GroupedAddressesView.cshtml @@ -0,0 +1,28 @@ +@model Esfa.Recruit.Employer.Web.ViewModels.Part1.Location.AddOneLocationViewModel + +@{ + var groupedLocations = Model.AvailableLocations.GroupByLastFilledAddressLine(); + var index = 0; + foreach (var group in groupedLocations) + { +

@group.Key

+ foreach (var location in group) + { + var address = location.Value.ToAddressString(); + var controlId = $"selectedLocation_{index++}"; + +
+ +
+ } + } +} \ No newline at end of file diff --git a/src/Employer/Employer.Web/Views/Part1/MultipleLocations/AddMoreThanOneLocation.cshtml b/src/Employer/Employer.Web/Views/Part1/MultipleLocations/AddMoreThanOneLocation.cshtml index 709f99b801..d5106dc2b1 100644 --- a/src/Employer/Employer.Web/Views/Part1/MultipleLocations/AddMoreThanOneLocation.cshtml +++ b/src/Employer/Employer.Web/Views/Part1/MultipleLocations/AddMoreThanOneLocation.cshtml @@ -1,5 +1,6 @@ @using Esfa.Recruit.Shared.Web.Domain @using Esfa.Recruit.Shared.Web.TagHelpers + @model Esfa.Recruit.Employer.Web.ViewModels.Part1.MultipleLocations.AddMoreThanOneLocationViewModel @{ @@ -24,6 +25,7 @@
+

@Model.ApprenticeshipTitle Add more than one location @@ -39,8 +41,13 @@

Select up to 10 locations

- - +
+ +
+ + +
+
diff --git a/src/Employer/Employer.Web/Views/Part1/MultipleLocations/_AddressesView.cshtml b/src/Employer/Employer.Web/Views/Part1/MultipleLocations/_AddressesView.cshtml index a426d9fae5..aa2198d0b2 100644 --- a/src/Employer/Employer.Web/Views/Part1/MultipleLocations/_AddressesView.cshtml +++ b/src/Employer/Employer.Web/Views/Part1/MultipleLocations/_AddressesView.cshtml @@ -1,27 +1,22 @@ @model Esfa.Recruit.Employer.Web.ViewModels.Part1.MultipleLocations.AddMoreThanOneLocationViewModel -
- -
- @{ - var index = 0; - foreach (var location in Model.AvailableLocations) - { - var address = location.ToAddressString(); - var controlId = $"selectedLocation_{index++}"; -
- - -
- } - } -
-
\ No newline at end of file +@{ + var index = 0; + foreach (var location in Model.AvailableLocations) + { + var address = location.ToAddressString(); + var controlId = $"selectedLocation_{index++}"; +
+ +
+ } +} \ No newline at end of file diff --git a/src/Employer/Employer.Web/Views/Part1/MultipleLocations/_GroupedAddressesView.cshtml b/src/Employer/Employer.Web/Views/Part1/MultipleLocations/_GroupedAddressesView.cshtml index 7992cb1a0b..1dd1ceab78 100644 --- a/src/Employer/Employer.Web/Views/Part1/MultipleLocations/_GroupedAddressesView.cshtml +++ b/src/Employer/Employer.Web/Views/Part1/MultipleLocations/_GroupedAddressesView.cshtml @@ -1,32 +1,27 @@ @model Esfa.Recruit.Employer.Web.ViewModels.Part1.MultipleLocations.AddMoreThanOneLocationViewModel -
- -
- @{ - var groupedLocations = Model.AvailableLocations.GroupByLastFilledAddressLine(); - var index = 0; - foreach (var group in groupedLocations) - { -

@group.Key

- foreach (var location in group) - { - var address = location.Value.ToAddressString(); - var controlId = $"selectedLocation_{index++}"; -
- - -
- } - } - } -
-
\ No newline at end of file +@{ + var groupedLocations = Model.AvailableLocations.GroupByLastFilledAddressLine(); + var index = 0; + foreach (var group in groupedLocations) + { +

@group.Key

+ foreach (var location in group) + { + var address = location.Value.ToAddressString(); + var controlId = $"selectedLocation_{index++}"; +
+ +
+ } + } +} \ No newline at end of file diff --git a/src/Employer/UnitTests/Employer.Web/Controllers/Part1/LocationControllerTests.cs b/src/Employer/UnitTests/Employer.Web/Controllers/Part1/LocationControllerTests.cs new file mode 100644 index 0000000000..54025875a4 --- /dev/null +++ b/src/Employer/UnitTests/Employer.Web/Controllers/Part1/LocationControllerTests.cs @@ -0,0 +1,175 @@ +using System.Collections.Generic; +using System.Linq; +using AutoFixture.NUnit3; +using Esfa.Recruit.Employer.Web; +using Esfa.Recruit.Employer.Web.Configuration; +using Esfa.Recruit.Employer.Web.Configuration.Routing; +using Esfa.Recruit.Employer.Web.Controllers.Part1; +using Esfa.Recruit.Employer.Web.RouteModel; +using Esfa.Recruit.Employer.Web.Services; +using Esfa.Recruit.Employer.Web.ViewModels.Part1.Location; +using Esfa.Recruit.Shared.Web.Extensions; +using Esfa.Recruit.Vacancies.Client.Application.Validation; +using Esfa.Recruit.Vacancies.Client.Domain.Entities; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ViewFeatures; + +namespace Esfa.Recruit.Employer.UnitTests.Employer.Web.Controllers.Part1; + +public class LocationControllerTests +{ + private LocationController _sut; + private Mock _vacancyLocationService; + + [SetUp] + public void SetUp() + { + _sut = new LocationController(Mock.Of()); + _sut.AddControllerContext().WithUser(Guid.NewGuid()); + var tempData = new TempDataDictionary(_sut.ControllerContext.HttpContext, Mock.Of()); + _sut.TempData = tempData; + + _vacancyLocationService = new Mock(); + } + + [Test, MoqAutoData] + public async Task When_Getting_AddOneLocation_View_Is_Returned([Frozen] Vacancy vacancy, [Frozen] Mock utility, VacancyRouteModel model, [Frozen] List
locations) + { + // arrange + model.VacancyId = vacancy.Id; + model.EmployerAccountId = vacancy.EmployerAccountId; + utility.Setup(x => x.GetAuthorisedVacancyForEditAsync(It.IsAny(), It.IsAny())).ReturnsAsync(vacancy); + _vacancyLocationService.Setup(x => x.GetVacancyLocations(It.Is(v => v == vacancy))).ReturnsAsync(locations); + + // act + var result = (await _sut.AddOneLocation(_vacancyLocationService.Object, utility.Object, model, true) as ViewResult)?.Model as AddOneLocationViewModel; + + // assert + result.Should().NotBeNull(); + result!.VacancyId.Should().Be(vacancy.Id); + result.ApprenticeshipTitle.Should().Be(vacancy.Title); + result.EmployerAccountId.Should().BeEquivalentTo(vacancy.EmployerAccountId); + result.AvailableLocations.Should().BeEquivalentTo(locations); + } + + [Test, MoqAutoData] + public async Task When_Getting_AddOneLocation_View_Is_Returned_With_Added_Address([Frozen] Vacancy vacancy, [Frozen] Mock utility, VacancyRouteModel model, [Frozen] List
locations) + { + // arrange + model.VacancyId = vacancy.Id; + model.EmployerAccountId = vacancy.EmployerAccountId; + utility.Setup(x => x.GetAuthorisedVacancyForEditAsync(It.IsAny(), It.IsAny())).ReturnsAsync(vacancy); + _vacancyLocationService.Setup(x => x.GetVacancyLocations(It.Is(v => v == vacancy))).ReturnsAsync(locations); + + const string newlyAddedAddress = "An Address"; + _sut.TempData[TempDataKeys.AddedLocation] = newlyAddedAddress; + + // act + var result = (await _sut.AddOneLocation(_vacancyLocationService.Object, utility.Object, model, true) as ViewResult)?.Model as AddOneLocationViewModel; + + // assert + result.Should().NotBeNull(); + result!.BannerAddress.Should().Be(newlyAddedAddress); + result.SelectedLocation.Should().Be(newlyAddedAddress); + } + + [Test, MoqAutoData] + public async Task When_Getting_AddOneLocation_SelectedLocation_Is_Null_When_Vacancy_Has_More_Than_One_Existing_Address([Frozen] Vacancy vacancy, [Frozen] Mock utility, VacancyRouteModel model, [Frozen] List
locations) + { + // arrange + model.VacancyId = vacancy.Id; + model.EmployerAccountId = vacancy.EmployerAccountId; + utility.Setup(x => x.GetAuthorisedVacancyForEditAsync(It.IsAny(), It.IsAny())).ReturnsAsync(vacancy); + _vacancyLocationService.Setup(x => x.GetVacancyLocations(It.Is(v => v == vacancy))).ReturnsAsync(locations); + + // act + var result = (await _sut.AddOneLocation(_vacancyLocationService.Object, utility.Object, model, true) as ViewResult)?.Model as AddOneLocationViewModel; + + // assert + result.Should().NotBeNull(); + result!.SelectedLocation.Should().BeNull(); + } + + [Test, MoqAutoData] + public async Task When_Getting_AddOneLocation_SelectedLocation_Is_Set_When_Vacancy_Has_Exactly_One_Existing_Address( + [Frozen] Vacancy vacancy, + [Frozen] Mock utility, + VacancyRouteModel model, + Address address, + [Frozen] List
locations) + { + // arrange + vacancy.EmployerLocations = [address]; + model.VacancyId = vacancy.Id; + model.EmployerAccountId = vacancy.EmployerAccountId; + utility.Setup(x => x.GetAuthorisedVacancyForEditAsync(It.IsAny(), It.IsAny())).ReturnsAsync(vacancy); + _vacancyLocationService.Setup(x => x.GetVacancyLocations(It.Is(v => v == vacancy))).ReturnsAsync(locations); + + // act + var result = (await _sut.AddOneLocation(_vacancyLocationService.Object, utility.Object, model, true) as ViewResult)?.Model as AddOneLocationViewModel; + + // assert + result.Should().NotBeNull(); + result!.SelectedLocation.Should().Be(address.ToAddressString()); + } + + [Test] + [MoqInlineAutoData(true, RouteNames.EmployerTaskListGet)] + [MoqInlineAutoData(false, RouteNames.EmployerCheckYourAnswersGet)] + public async Task When_Posting_AddOneLocation_With_Valid_Model_Then_Redirected(bool wizard, string expectedRouteName, + [Frozen] Vacancy vacancy, [Frozen] Mock utility, [Frozen] List
locations) + { + // arrange + var model = new AddOneLocationEditModel + { + VacancyId = vacancy.Id, + EmployerAccountId = vacancy.EmployerAccountId, + SelectedLocation = locations.First().ToAddressString() + }; + utility.Setup(x => x.GetAuthorisedVacancyForEditAsync(It.IsAny(), It.IsAny())).ReturnsAsync(vacancy); + _vacancyLocationService.Setup(x => x.GetVacancyLocations(It.Is(v => v == vacancy))).ReturnsAsync(locations); + _vacancyLocationService.Setup(x => x.UpdateDraftVacancyLocations(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>(), null)) + .ReturnsAsync(new UpdateVacancyLocationsResult(null)); + + // act + var result = await _sut.AddOneLocation(_vacancyLocationService.Object, utility.Object, model, wizard) as RedirectToRouteResult; + + // assert + result.Should().NotBeNull(); + result!.RouteName.Should().Be(expectedRouteName); + } + + [Test, MoqAutoData] + public async Task When_Posting_AddOneLocation_With_Invalid_Model_Then_View_Returned([Frozen] Vacancy vacancy, [Frozen] Mock utility, [Frozen] List
locations) + { + // arrange + var model = new AddOneLocationEditModel + { + VacancyId = vacancy.Id, + EmployerAccountId = vacancy.EmployerAccountId, + SelectedLocation = "invalid" + }; + utility.Setup(x => x.GetAuthorisedVacancyForEditAsync(It.IsAny(), It.IsAny())).ReturnsAsync(vacancy); + _vacancyLocationService.Setup(x => x.GetVacancyLocations(It.Is(v => v == vacancy))).ReturnsAsync(locations); + _vacancyLocationService.Setup(x => x.UpdateDraftVacancyLocations(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>(), null)) + .ReturnsAsync(new UpdateVacancyLocationsResult(new EntityValidationResult { Errors = [new EntityValidationError(1, "EmployerLocations", "error message", "code")] })); + + // act + var result = (await _sut.AddOneLocation(_vacancyLocationService.Object, utility.Object, model, true) as ViewResult)?.Model as AddOneLocationViewModel; + var errorResult = _sut.ModelState.GetValueOrDefault("SelectedLocation"); + + // assert + result.Should().NotBeNull(); + result!.VacancyId.Should().Be(vacancy.Id); + result.ApprenticeshipTitle.Should().Be(vacancy.Title); + result.EmployerAccountId.Should().BeEquivalentTo(vacancy.EmployerAccountId); + result.AvailableLocations.Should().BeEquivalentTo(locations); + result.SelectedLocation.Should().Be("invalid"); + _sut.ModelState.Values.Should().HaveCount(1); + errorResult.Should().NotBeNull(); + errorResult.Errors.Should().HaveCount(1); + errorResult.Errors.First().ErrorMessage.Should().Be("error message"); + + } +} \ No newline at end of file diff --git a/src/Employer/UnitTests/Employer.Web/Controllers/Part1/MultipleLocationsControllerTests.cs b/src/Employer/UnitTests/Employer.Web/Controllers/Part1/MultipleLocationsControllerTests.cs index defc23a196..ed4a04f2ea 100644 --- a/src/Employer/UnitTests/Employer.Web/Controllers/Part1/MultipleLocationsControllerTests.cs +++ b/src/Employer/UnitTests/Employer.Web/Controllers/Part1/MultipleLocationsControllerTests.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using System.Text.Json; using Microsoft.AspNetCore.Mvc; using AutoFixture.NUnit3; @@ -7,11 +6,9 @@ using Esfa.Recruit.Employer.Web.Configuration; using Esfa.Recruit.Employer.Web.Configuration.Routing; using Esfa.Recruit.Employer.Web.Controllers.Part1; -using Esfa.Recruit.Employer.Web.Orchestrators.Part1; using Esfa.Recruit.Employer.Web.RouteModel; +using Esfa.Recruit.Employer.Web.Services; using Esfa.Recruit.Employer.Web.ViewModels.Part1.MultipleLocations; -using Esfa.Recruit.Shared.Web.Orchestrators; -using Esfa.Recruit.Vacancies.Client.Application.Validation; using Esfa.Recruit.Vacancies.Client.Domain.Entities; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.ViewFeatures; @@ -89,31 +86,43 @@ public async Task When_Posting_LocationsAvailability_With_Invalid_state_Then_You [Test, MoqAutoData] public async Task When_Getting_AddMoreThanOneLocation_Then_The_View_Is_Returned( - Mock orchestrator, - AddMoreThanOneLocationViewModel viewModel, + Vacancy vacancy, + [Frozen] List
locations, + Mock vacancyLocationService, + Mock utility, + VacancyRouteModel model, [Greedy] MultipleLocationsController sut) { // arrange var httpContext = new DefaultHttpContext(); var tempData = new TempDataDictionary(httpContext, Mock.Of()); sut.TempData = tempData; + sut.AddControllerContext().WithUser(Guid.NewGuid()); - orchestrator - .Setup(x => x.GetAddMoreThanOneLocationViewModelAsync(It.IsAny(), It.Is(wizard => wizard == true))) - .ReturnsAsync(viewModel); + vacancy.EmployerAccountId = model.EmployerAccountId; + vacancy.Id = model.VacancyId; + utility.Setup(x => x.GetAuthorisedVacancyForEditAsync(It.IsAny(), It.IsAny())).ReturnsAsync(vacancy); + vacancyLocationService.Setup(x => x.GetVacancyLocations(It.Is(v => v == vacancy))).ReturnsAsync(locations); // act - var result = (await sut.AddMoreThanOneLocation(orchestrator.Object, new VacancyRouteModel(), true) as ViewResult)?.Model as AddMoreThanOneLocationViewModel; + var result = (await sut.AddMoreThanOneLocation(vacancyLocationService.Object, utility.Object, model, true) as ViewResult)?.Model as AddMoreThanOneLocationViewModel; // assert result.Should().NotBeNull(); - result.Should().Be(viewModel); + result!.ApprenticeshipTitle.Should().Be(vacancy.Title); + result.AvailableLocations.Should().BeEquivalentTo(locations); + result.EmployerAccountId.Should().Be(vacancy.EmployerAccountId); + result.VacancyId.Should().Be(vacancy.Id); + result.SelectedLocations.Should().BeEquivalentTo(vacancy.EmployerLocations, options => options.ExcludingMissingMembers()); } [Test, MoqAutoData] public async Task When_Getting_AddMoreThanOneLocation_Having_Returned_From_The_Add_New_Location_Pathway_Then_The_Previously_Selected_Values_Are_Restored( - Mock orchestrator, - AddMoreThanOneLocationViewModel viewModel, + Vacancy vacancy, + [Frozen] List
locations, + Mock vacancyLocationService, + Mock utility, + VacancyRouteModel model, [Greedy] MultipleLocationsController sut) { // arrange @@ -123,63 +132,78 @@ public async Task When_Getting_AddMoreThanOneLocation_Having_Returned_From_The_A [TempDataKeys.SelectedLocations] = JsonSerializer.Serialize(selected) }; sut.TempData = tempData; + + sut.AddControllerContext().WithUser(Guid.NewGuid()); - orchestrator - .Setup(x => x.GetAddMoreThanOneLocationViewModelAsync(It.IsAny(), It.Is(wizard => wizard == true))) - .ReturnsAsync(viewModel); + vacancy.EmployerAccountId = model.EmployerAccountId; + vacancy.Id = model.VacancyId; + utility.Setup(x => x.GetAuthorisedVacancyForEditAsync(It.IsAny(), It.IsAny())).ReturnsAsync(vacancy); + vacancyLocationService.Setup(x => x.GetVacancyLocations(It.Is(v => v == vacancy))).ReturnsAsync(locations); // act - var result = (await sut.AddMoreThanOneLocation(orchestrator.Object, new VacancyRouteModel(), true) as ViewResult)?.Model as AddMoreThanOneLocationViewModel; - + var result = (await sut.AddMoreThanOneLocation(vacancyLocationService.Object, utility.Object, model, true) as ViewResult)?.Model as AddMoreThanOneLocationViewModel; + // assert result.Should().NotBeNull(); result!.SelectedLocations.Should().BeEquivalentTo(selected); } - + [Test, MoqAutoData] public async Task When_Posting_AddMoreThanOneLocation_With_Invalid_state_Then_The_View_Is_Returned( - Mock orchestrator, - AddMoreThanOneLocationEditModel editModel, + Vacancy vacancy, + [Frozen] List
locations, + Mock vacancyLocationService, + Mock utility, + AddMoreThanOneLocationEditModel model, [Greedy] MultipleLocationsController sut) { // arrange - var error = new EntityValidationError(1, "property-name", "error-message", "error-code"); - var orchestratorResponse = new OrchestratorResponse(new EntityValidationResult { Errors = [error] }); + var httpContext = new DefaultHttpContext(); + var tempData = new TempDataDictionary(httpContext, Mock.Of()); + sut.TempData = tempData; sut.AddControllerContext().WithUser(Guid.NewGuid()); - orchestrator - .Setup(x => x.PostAddMoreThanOneLocationViewModelAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(orchestratorResponse); - + + vacancy.EmployerAccountId = model.EmployerAccountId; + vacancy.Id = model.VacancyId; + utility.Setup(x => x.GetAuthorisedVacancyForEditAsync(It.IsAny(), It.IsAny())).ReturnsAsync(vacancy); + vacancyLocationService.Setup(x => x.GetVacancyLocations(It.Is(v => v == vacancy))).ReturnsAsync(locations); + // act - var result = await sut.AddMoreThanOneLocation(orchestrator.Object, editModel, true) as ViewResult; - + var result = (await sut.AddMoreThanOneLocation(vacancyLocationService.Object, utility.Object, model, true) as ViewResult)?.Model as AddMoreThanOneLocationViewModel; + // assert result.Should().NotBeNull(); - result!.Model.Should().NotBeNull(); - result.Model.Should().BeOfType(); - sut.ControllerContext.ModelState.Count.Should().Be(1); - sut.ControllerContext.ModelState.First().Key.Should().Be(error.PropertyName); - sut.ControllerContext.ModelState.First().Value.Errors.First().ErrorMessage.Should().Be(error.ErrorMessage); + result!.ApprenticeshipTitle.Should().Be(vacancy.Title); + result.AvailableLocations.Should().BeEquivalentTo(locations); + result.EmployerAccountId.Should().Be(vacancy.EmployerAccountId); + result.VacancyId.Should().Be(vacancy.Id); + result.SelectedLocations.Should().BeEquivalentTo(model.SelectedLocations); } [Test, MoqAutoData] public async Task When_Posting_AddMoreThanOneLocation_With_Valid_state_Then_You_Are_Redirected_To_Confirmation_Route( - Mock orchestrator, - AddMoreThanOneLocationEditModel editModel, + Vacancy vacancy, + [Frozen] List
locations, + Mock vacancyLocationService, + Mock utility, + AddMoreThanOneLocationEditModel model, [Greedy] MultipleLocationsController sut) { // arrange sut.AddControllerContext().WithUser(Guid.NewGuid()); - orchestrator - .Setup(x => x.PostAddMoreThanOneLocationViewModelAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(new OrchestratorResponse(false)); + vacancy.EmployerAccountId = model.EmployerAccountId; + vacancy.Id = model.VacancyId; + utility.Setup(x => x.GetAuthorisedVacancyForEditAsync(It.IsAny(), It.IsAny())).ReturnsAsync(vacancy); + vacancyLocationService.Setup(x => x.GetVacancyLocations(It.Is(v => v == vacancy))).ReturnsAsync(locations); + vacancyLocationService.Setup(x => x.UpdateDraftVacancyLocations(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>(), null)) + .ReturnsAsync(new UpdateVacancyLocationsResult(null)); // act - var result = (await sut.AddMoreThanOneLocation(orchestrator.Object, editModel, true) as ViewResult)?.Model as AddMoreThanOneLocationViewModel; + var result = await sut.AddMoreThanOneLocation(vacancyLocationService.Object, utility.Object, model, true) as RedirectToRouteResult; // assert result.Should().NotBeNull(); - result!.SelectedLocations.Should().BeEquivalentTo(editModel.SelectedLocations); + result!.RouteName.Should().Be(RouteNames.MultipleLocationsConfirm_Get); } [Test, MoqAutoData] diff --git a/src/Employer/UnitTests/Employer.Web/Controllers/Part1/RecruitNationallyControllerTests.cs b/src/Employer/UnitTests/Employer.Web/Controllers/Part1/RecruitNationallyControllerTests.cs index ed5b7b2b41..5503b85c33 100644 --- a/src/Employer/UnitTests/Employer.Web/Controllers/Part1/RecruitNationallyControllerTests.cs +++ b/src/Employer/UnitTests/Employer.Web/Controllers/Part1/RecruitNationallyControllerTests.cs @@ -1,31 +1,42 @@ -using AutoFixture.NUnit3; +using System.Collections.Generic; +using AutoFixture.NUnit3; using Esfa.Recruit.Employer.Web; using Esfa.Recruit.Employer.Web.Configuration.Routing; using Esfa.Recruit.Employer.Web.Controllers.Part1; using Esfa.Recruit.Employer.Web.Models.AddLocation; using Esfa.Recruit.Employer.Web.RouteModel; +using Esfa.Recruit.Employer.Web.Services; using Esfa.Recruit.Employer.Web.ViewModels.Part1.RecruitNationally; using Esfa.Recruit.Vacancies.Client.Application.Validation; using Esfa.Recruit.Vacancies.Client.Domain.Entities; -using Esfa.Recruit.Vacancies.Client.Infrastructure.Client; using Microsoft.AspNetCore.Mvc; namespace Esfa.Recruit.Employer.UnitTests.Employer.Web.Controllers.Part1; public class RecruitNationallyControllerTests { + private RecruitNationallyController _sut; + private Mock _vacancyLocationService; + + [SetUp] + public void SetUp() + { + _sut = new RecruitNationallyController(); + _sut.AddControllerContext().WithUser(Guid.NewGuid()); + + _vacancyLocationService = new Mock(); + } + + [Test, MoqAutoData] - public async Task When_Getting_RecruitNationally_Then_ViewModel_Is_Returned( - [Frozen] Vacancy vacancy, - Mock utility, - [Greedy] RecruitNationallyController sut) + public async Task When_Getting_RecruitNationally_Then_ViewModel_Is_Returned([Frozen] Vacancy vacancy, Mock utility) { // arrange var vacancyRouteModel = new VacancyRouteModel { VacancyId = vacancy.Id, EmployerAccountId = vacancy.EmployerAccountId, }; utility.Setup(x => x.GetAuthorisedVacancyForEditAsync(It.IsAny(), It.IsAny())).ReturnsAsync(vacancy); // act - var result = (await sut.RecruitNationally(utility.Object, vacancyRouteModel) as ViewResult)?.Model as RecruitNationallyViewModel; + var result = (await _sut.RecruitNationally(utility.Object, vacancyRouteModel) as ViewResult)?.Model as RecruitNationallyViewModel; // assert result.Should().NotBeNull(); @@ -36,14 +47,12 @@ public async Task When_Getting_RecruitNationally_Then_ViewModel_Is_Returned( } [Test, MoqAutoData] - public async Task When_Posting_Invalid_Model_To_RecruitNationally_Then_ViewModel_Is_Returned( - [Frozen] Vacancy vacancy, - Mock utility, - IRecruitVacancyClient recruitVacancyClient, - [Greedy] RecruitNationallyController sut) + public async Task When_Posting_Invalid_Model_To_RecruitNationally_Then_ViewModel_Is_Returned([Frozen] Vacancy vacancy, Mock utility) { // arrange utility.Setup(x => x.GetAuthorisedVacancyForEditAsync(It.IsAny(), It.IsAny())).ReturnsAsync(vacancy); + _vacancyLocationService.Setup(x => x.UpdateDraftVacancyLocations(It.IsAny(), It.IsAny(), It.IsAny(), null, It.IsAny())) + .ReturnsAsync(new UpdateVacancyLocationsResult(new EntityValidationResult { Errors = new List { new (1, "propertyName", "errorMessage", "") } })); var model = new RecruitNationallyEditModel { AdditionalInformation = null, @@ -52,29 +61,22 @@ public async Task When_Posting_Invalid_Model_To_RecruitNationally_Then_ViewModel }; // act - var result = (await sut.RecruitNationally(recruitVacancyClient, utility.Object, model) as ViewResult)?.Model as RecruitNationallyViewModel; + var result = (await _sut.RecruitNationally(_vacancyLocationService.Object, utility.Object, model) as ViewResult)?.Model as RecruitNationallyViewModel; // assert result.Should().NotBeNull(); result!.VacancyId.Should().Be(vacancy.Id); result.ApprenticeshipTitle.Should().Be(vacancy.Title); result.EmployerAccountId.Should().BeEquivalentTo(vacancy.EmployerAccountId); - result.AdditionalInformation.Should().Be(vacancy.EmployerLocationInformation); + result.AdditionalInformation.Should().BeNull(); } [Test] [MoqInlineAutoData(true, RouteNames.EmployerTaskListGet)] [MoqInlineAutoData(false, RouteNames.EmployerCheckYourAnswersGet)] - public async Task When_Posting_Valid_Model_To_RecruitNationally_Then_RedirectToRoute_Is_Return( - bool isWizard, - string expectedRouteName, - [Frozen] Vacancy vacancy, - Mock utility, - Mock recruitVacancyClient, - [Greedy] RecruitNationallyController sut) + public async Task When_Posting_Valid_Model_To_RecruitNationally_Then_RedirectToRoute_Is_Return(bool isWizard, string expectedRouteName, [Frozen] Vacancy vacancy, Mock utility) { // arrange - sut.AddControllerContext().WithUser(Guid.NewGuid()); utility.Setup(x => x.GetAuthorisedVacancyForEditAsync(It.IsAny(), It.IsAny())).ReturnsAsync(vacancy); var model = new RecruitNationallyEditModel { @@ -82,17 +84,15 @@ public async Task When_Posting_Valid_Model_To_RecruitNationally_Then_RedirectToR VacancyId = vacancy.Id, EmployerAccountId = vacancy.EmployerAccountId }; - - recruitVacancyClient - .Setup(x => x.Validate(It.IsAny(), It.IsAny())) - .Returns(new EntityValidationResult()); + + _vacancyLocationService.Setup(x => x.UpdateDraftVacancyLocations(It.IsAny(), It.IsAny(), It.IsAny(), null, It.IsAny())) + .ReturnsAsync(new UpdateVacancyLocationsResult(null)); // act - var result = await sut.RecruitNationally(recruitVacancyClient.Object, utility.Object, model, isWizard) as RedirectToRouteResult; + var result = await _sut.RecruitNationally(_vacancyLocationService.Object, utility.Object, model, isWizard) as RedirectToRouteResult; // assert result.Should().NotBeNull(); result!.RouteName.Should().Be(expectedRouteName); - recruitVacancyClient.Verify(x => x.UpdateDraftVacancyAsync(vacancy, It.IsAny())); } } \ No newline at end of file diff --git a/src/Shared/Recruit.Vacancies.Client/Application/Validation/Fluent/FluentVacancyValidator.cs b/src/Shared/Recruit.Vacancies.Client/Application/Validation/Fluent/FluentVacancyValidator.cs index 7fa286e564..4bc2671226 100644 --- a/src/Shared/Recruit.Vacancies.Client/Application/Validation/Fluent/FluentVacancyValidator.cs +++ b/src/Shared/Recruit.Vacancies.Client/Application/Validation/Fluent/FluentVacancyValidator.cs @@ -264,20 +264,18 @@ private void ValidateOrganisation() .RunCondition(VacancyRuleSet.EmployerAddress); }); - // TODO: this will be required for the other validation scenarios - // When(v => v.EmployerLocationOption == AvailableWhere.OneLocation, () => - // { - // RuleFor(x => x.EmployerLocations) - // .NotNull() - // .Must(x => x.Count == 1) - // .WithMessage("Select a location") - // .WithState(_ => VacancyRuleSet.EmployerAddress) - // .RunCondition(VacancyRuleSet.EmployerAddress); - // - // RuleForEach(x => x.EmployerLocations) - // .SetValidator(new AddressValidator((long)VacancyRuleSet.EmployerAddress)) - // .RunCondition(VacancyRuleSet.EmployerAddress); - // }); + When(v => v.EmployerLocationOption == AvailableWhere.OneLocation, () => + { + RuleFor(x => x.EmployerLocations) + .Must(x => x is { Count: 1 }) + .WithMessage("Select a location") + .WithState(_ => VacancyRuleSet.EmployerAddress) + .RunCondition(VacancyRuleSet.EmployerAddress); + + RuleForEach(x => x.EmployerLocations) + .SetValidator(new AddressValidator((long)VacancyRuleSet.EmployerAddress)) + .RunCondition(VacancyRuleSet.EmployerAddress); + }); When(v => v.EmployerLocationOption == AvailableWhere.MultipleLocations, () => { From fb61ae8d6f9bdaa37610ecb5ab1dcc9eacfe692f Mon Sep 17 00:00:00 2001 From: killij <51908793+killij@users.noreply.github.com> Date: Tue, 24 Dec 2024 14:53:02 +0000 Subject: [PATCH 2/3] FAI-2175 - Sonar suggestions --- .../Controllers/Part1/LocationController.cs | 14 +++++--------- .../Services/VacancyLocationService.cs | 6 +++--- .../Views/Part1/Location/AddOneLocation.cshtml | 2 +- .../Part1/Location/_GroupedAddressesView.cshtml | 6 +++--- .../MultipleLocations/_GroupedAddressesView.cshtml | 6 +++--- 5 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/Employer/Employer.Web/Controllers/Part1/LocationController.cs b/src/Employer/Employer.Web/Controllers/Part1/LocationController.cs index d31e1d20fe..71a1fec59c 100644 --- a/src/Employer/Employer.Web/Controllers/Part1/LocationController.cs +++ b/src/Employer/Employer.Web/Controllers/Part1/LocationController.cs @@ -91,7 +91,7 @@ public async Task GetAddresses([FromServices] LocationOrchestrato } #endregion - + [FeatureGate(FeatureNames.MultipleLocations)] [HttpGet("add-one-location", Name = RouteNames.AddOneLocation_Get)] public async Task AddOneLocation( @@ -102,7 +102,7 @@ public async Task AddOneLocation( { var vacancy = await utility.GetAuthorisedVacancyForEditAsync(vacancyRouteModel, RouteNames.AddOneLocation_Get); var allLocations = await vacancyLocationService.GetVacancyLocations(vacancy); - var selectedLocation = vacancy.EmployerLocations is { Count: 1 } ? vacancy.EmployerLocations.First() : null; + var selectedLocation = vacancy.EmployerLocations is { Count: 1 } ? vacancy.EmployerLocations[0] : null; var viewModel = new AddOneLocationViewModel { @@ -123,11 +123,6 @@ public async Task AddOneLocation( return View(viewModel); } - private static readonly Dictionary ValidationFieldMappings = new() - { - { "EmployerLocations", "SelectedLocation" } - }; - [FeatureGate(FeatureNames.MultipleLocations)] [HttpPost("add-one-location", Name = RouteNames.AddOneLocation_Post)] public async Task AddOneLocation( @@ -152,11 +147,12 @@ public async Task AddOneLocation( : RedirectToRoute(RouteNames.EmployerCheckYourAnswersGet, new { model.VacancyId, model.EmployerAccountId }); } - ModelState.AddValidationErrors(result.ValidationResult, ValidationFieldMappings); + + ModelState.AddValidationErrors(result.ValidationResult, new Dictionary { { "EmployerLocations", "SelectedLocation" } }); var viewModel = new AddOneLocationViewModel { ApprenticeshipTitle = vacancy.Title, - AvailableLocations = allLocations ?? [], + AvailableLocations = allLocations, VacancyId = model.VacancyId, EmployerAccountId = model.EmployerAccountId, PageInfo = utility.GetPartOnePageInfo(vacancy), diff --git a/src/Employer/Employer.Web/Services/VacancyLocationService.cs b/src/Employer/Employer.Web/Services/VacancyLocationService.cs index 52b0d0e999..e21e8e074d 100644 --- a/src/Employer/Employer.Web/Services/VacancyLocationService.cs +++ b/src/Employer/Employer.Web/Services/VacancyLocationService.cs @@ -12,7 +12,7 @@ namespace Esfa.Recruit.Employer.Web.Services; public interface IVacancyLocationService { public Task> GetVacancyLocations(Vacancy vacancy); - public Task UpdateDraftVacancyLocations(Vacancy vacancy, VacancyUser user, AvailableWhere availableWhere, List
locations, string locationInformation = null); + public Task UpdateDraftVacancyLocations(Vacancy vacancy, VacancyUser user, AvailableWhere availableWhere, List
locations = null, string locationInformation = null); } public record UpdateVacancyLocationsResult(EntityValidationResult ValidationResult); @@ -29,10 +29,10 @@ public async Task> GetVacancyLocations(Vacancy vacancy) var locations = new List
(); var legalAddress = legalEntity.Address.ConvertToDomainAddress(); - if (legalAddress.IsEmpty() is false) + if (!legalAddress.IsEmpty()) { locations.Add(legalAddress); - }; + } locations.AddRange(employerProfile.OtherLocations.Where(x => !x.IsEmpty())); return locations; } diff --git a/src/Employer/Employer.Web/Views/Part1/Location/AddOneLocation.cshtml b/src/Employer/Employer.Web/Views/Part1/Location/AddOneLocation.cshtml index 8cc4f428ec..be744858fb 100644 --- a/src/Employer/Employer.Web/Views/Part1/Location/AddOneLocation.cshtml +++ b/src/Employer/Employer.Web/Views/Part1/Location/AddOneLocation.cshtml @@ -48,7 +48,7 @@
diff --git a/src/Employer/Employer.Web/Views/Part1/Location/_GroupedAddressesView.cshtml b/src/Employer/Employer.Web/Views/Part1/Location/_GroupedAddressesView.cshtml index 0d08b3c7a6..55959abb46 100644 --- a/src/Employer/Employer.Web/Views/Part1/Location/_GroupedAddressesView.cshtml +++ b/src/Employer/Employer.Web/Views/Part1/Location/_GroupedAddressesView.cshtml @@ -6,9 +6,9 @@ foreach (var group in groupedLocations) {

@group.Key

- foreach (var location in group) + foreach (var location in group.Select(x => x.Value)) { - var address = location.Value.ToAddressString(); + var address = location.ToAddressString(); var controlId = $"selectedLocation_{index++}";
@@ -20,7 +20,7 @@ type="radio" checked="@(Model.SelectedLocation == address)">
} diff --git a/src/Employer/Employer.Web/Views/Part1/MultipleLocations/_GroupedAddressesView.cshtml b/src/Employer/Employer.Web/Views/Part1/MultipleLocations/_GroupedAddressesView.cshtml index 1dd1ceab78..9d52709e0f 100644 --- a/src/Employer/Employer.Web/Views/Part1/MultipleLocations/_GroupedAddressesView.cshtml +++ b/src/Employer/Employer.Web/Views/Part1/MultipleLocations/_GroupedAddressesView.cshtml @@ -6,9 +6,9 @@ foreach (var group in groupedLocations) {

@group.Key

- foreach (var location in group) + foreach (var location in group.Select(x => x.Value)) { - var address = location.Value.ToAddressString(); + var address = location.ToAddressString(); var controlId = $"selectedLocation_{index++}";
} From 53ea9a343c74a1a263aaffffdc7a5a6f7dffb8a5 Mon Sep 17 00:00:00 2001 From: killij <51908793+killij@users.noreply.github.com> Date: Thu, 2 Jan 2025 10:03:51 +0000 Subject: [PATCH 3/3] FAI-2175 - Remove wizard back link logic in location journey - should always return to previous page in journey --- .../Employer.Web/Views/Part1/Location/AddOneLocation.cshtml | 3 +-- .../Part1/MultipleLocations/AddMoreThanOneLocation.cshtml | 3 +-- .../Views/Part1/MultipleLocations/ConfirmLocations.cshtml | 3 +-- .../Views/Part1/RecruitNationally/RecruitNationally.cshtml | 3 +-- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/Employer/Employer.Web/Views/Part1/Location/AddOneLocation.cshtml b/src/Employer/Employer.Web/Views/Part1/Location/AddOneLocation.cshtml index be744858fb..7dbf072e66 100644 --- a/src/Employer/Employer.Web/Views/Part1/Location/AddOneLocation.cshtml +++ b/src/Employer/Employer.Web/Views/Part1/Location/AddOneLocation.cshtml @@ -10,8 +10,7 @@ } @section BackLink { - Back - Back + Back }
diff --git a/src/Employer/Employer.Web/Views/Part1/MultipleLocations/AddMoreThanOneLocation.cshtml b/src/Employer/Employer.Web/Views/Part1/MultipleLocations/AddMoreThanOneLocation.cshtml index d5106dc2b1..2c54ad7d7b 100644 --- a/src/Employer/Employer.Web/Views/Part1/MultipleLocations/AddMoreThanOneLocation.cshtml +++ b/src/Employer/Employer.Web/Views/Part1/MultipleLocations/AddMoreThanOneLocation.cshtml @@ -10,8 +10,7 @@ } @section BackLink { - Back - Back + Back }
diff --git a/src/Employer/Employer.Web/Views/Part1/MultipleLocations/ConfirmLocations.cshtml b/src/Employer/Employer.Web/Views/Part1/MultipleLocations/ConfirmLocations.cshtml index 13cc5c5aba..11feb30e3d 100644 --- a/src/Employer/Employer.Web/Views/Part1/MultipleLocations/ConfirmLocations.cshtml +++ b/src/Employer/Employer.Web/Views/Part1/MultipleLocations/ConfirmLocations.cshtml @@ -10,8 +10,7 @@ } @section BackLink { - Back - Back + Back }
diff --git a/src/Employer/Employer.Web/Views/Part1/RecruitNationally/RecruitNationally.cshtml b/src/Employer/Employer.Web/Views/Part1/RecruitNationally/RecruitNationally.cshtml index 652b9dc8ab..0a5ed239a9 100644 --- a/src/Employer/Employer.Web/Views/Part1/RecruitNationally/RecruitNationally.cshtml +++ b/src/Employer/Employer.Web/Views/Part1/RecruitNationally/RecruitNationally.cshtml @@ -7,8 +7,7 @@ } @section BackLink { - Back - Back + Back }