Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FAI-2175 - Create the new "Add a location" page #1543

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion src/Employer/Employer.Web/Configuration/IoC.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ private static void RegisterServiceDeps(IServiceCollection services, IConfigurat
services.AddTransient<IUtility, Utility>();
services.AddTransient<IFieldReviewHelper, FieldReviewHelper>();
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
services.AddSingleton<IVacancyLocationService, VacancyLocationService>();
}

private static void RegisterFluentValidators(IServiceCollection services)
Expand Down Expand Up @@ -140,7 +141,6 @@ private static void RegisterOrchestratorDeps(IServiceCollection services)
services.AddTransient<VacancyTaskListOrchestrator>();
services.AddTransient<IFutureProspectsOrchestrator, FutureProspectsOrchestrator>();
services.AddTransient<IAdditionalQuestionsOrchestrator, AdditionalQuestionsOrchestrator>();
services.AddTransient<IMultipleLocationsOrchestrator, MultipleLocationsOrchestrator>();
}

private static void RegisterMapperDeps(IServiceCollection services)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public async Task<IActionResult> 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 });
}

Expand Down
109 changes: 89 additions & 20 deletions src/Employer/Employer.Web/Controllers/Part1/LocationController.cs
Original file line number Diff line number Diff line change
@@ -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<IActionResult> Location(VacancyRouteModel vrm, [FromQuery] string wizard = "true")
public async Task<IActionResult> 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);

Expand All @@ -42,17 +39,17 @@ public async Task<IActionResult> 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<IActionResult> Location(LocationEditModel model, [FromQuery] bool wizard)
public async Task<IActionResult> 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)
{
Expand All @@ -61,7 +58,7 @@ public async Task<IActionResult> 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;
Expand All @@ -87,10 +84,82 @@ public IActionResult Cancel(VacancyRouteModel vrm, [FromQuery] bool wizard)
}

[HttpGet("location/GetAddresses")]
public async Task<IActionResult> GetAddresses([FromQuery] string searchTerm)
public async Task<IActionResult> 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<IActionResult> 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[0] : 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);
}

[FeatureGate(FeatureNames.MultipleLocations)]
[HttpPost("add-one-location", Name = RouteNames.AddOneLocation_Post)]
public async Task<IActionResult> 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, new Dictionary<string, string> { { "EmployerLocations", "SelectedLocation" } });
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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -43,7 +43,7 @@ public async Task<IActionResult> 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(),
Expand Down Expand Up @@ -73,18 +73,32 @@ private static async Task<LocationAvailabilityViewModel> GetLocationAvailability
[FeatureGate(FeatureNames.MultipleLocations)]
[HttpGet("add-many-locations", Name = RouteNames.AddMoreThanOneLocation_Get)]
public async Task<IActionResult> 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<List<string>>(value);
}

_ when TempData[TempDataKeys.SelectedLocations] is string value => JsonSerializer.Deserialize<List<string>>(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);
Expand All @@ -94,28 +108,52 @@ public async Task<IActionResult> AddMoreThanOneLocation(
return View(viewModel);
}

private static readonly Dictionary<string, string> ValidationFieldMappings = new()
{
{ "EmployerLocations", "SelectedLocations" }
};

[FeatureGate(FeatureNames.MultipleLocations)]
[HttpPost("add-many-locations", Name = RouteNames.AddMoreThanOneLocation_Post)]
public async Task<IActionResult> 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);
Expand Down
Loading
Loading