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

Induction journey #1748

Merged
merged 27 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ public static bool ValidateInductionData(
{
var requiresStartDate = status.RequiresStartDate();
var requiresCompletedDate = status.RequiresCompletedDate();
var requiresExemptionReason = status.RequiresExemptionReasons();

if (requiresStartDate && startDate is null)
{
Expand All @@ -305,13 +306,13 @@ public static bool ValidateInductionData(
return false;
}

if (status is InductionStatus.Exempt && exemptionReasons == InductionExemptionReasons.None)
if (requiresExemptionReason && exemptionReasons == InductionExemptionReasons.None)
{
error = $"Exemption reasons cannot be {nameof(InductionExemptionReasons.None)} when the status is: '{status}'.";
return false;
}

if (status is not InductionStatus.Exempt && exemptionReasons != InductionExemptionReasons.None)
if (!requiresExemptionReason && exemptionReasons != InductionExemptionReasons.None)
{
error = $"Exemption reasons must be {nameof(InductionExemptionReasons.None)} when the status is: '{status}'.";
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public enum InductionStatus
None = 0,
[InductionStatusInfo("required to complete", requiresStartDate: false, requiresCompletedDate: false)]
RequiredToComplete = 1,
[InductionStatusInfo("exempt", requiresStartDate: false, requiresCompletedDate: false)]
[InductionStatusInfo("exempt", requiresStartDate: false, requiresCompletedDate: false, requiresExemptionReasons: true)]
Exempt = 2,
[InductionStatusInfo("in progress", requiresStartDate: true, requiresCompletedDate: false)]
InProgress = 3,
Expand All @@ -35,6 +35,8 @@ public static class InductionStatusRegistry

public static bool RequiresCompletedDate(this InductionStatus status) => _info[status].RequiresCompletedDate;

public static bool RequiresExemptionReasons(this InductionStatus status) => _info[status].RequiresExemptionReasons;

public static InductionStatus ToInductionStatus(this dfeta_InductionStatus status) =>
ToInductionStatus((dfeta_InductionStatus?)status);

Expand All @@ -61,19 +63,20 @@ private static InductionStatusInfo GetInfo(InductionStatus status)
.GetCustomAttribute<InductionStatusInfoAttribute>() ??
throw new Exception($"{nameof(InductionStatus)}.{status} is missing the {nameof(InductionStatusInfoAttribute)} attribute.");

return new InductionStatusInfo(status, attr.Name, attr.RequiresStartDate, attr.RequiresCompletedDate);
return new InductionStatusInfo(status, attr.Name, attr.RequiresStartDate, attr.RequiresCompletedDate, attr.RequiresExemptionReason);
}
}

public sealed record InductionStatusInfo(InductionStatus Value, string Name, bool RequiresStartDate, bool RequiresCompletedDate)
public sealed record InductionStatusInfo(InductionStatus Value, string Name, bool RequiresStartDate, bool RequiresCompletedDate, bool RequiresExemptionReasons = false)
{
public string Title => Name[0..1].ToUpper() + Name[1..];
}

[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
file sealed class InductionStatusInfoAttribute(string name, bool requiresStartDate, bool requiresCompletedDate) : Attribute
file sealed class InductionStatusInfoAttribute(string name, bool requiresStartDate, bool requiresCompletedDate, bool requiresExemptionReasons = false) : Attribute
{
public string Name => name;
public bool RequiresStartDate => requiresStartDate;
public bool RequiresCompletedDate => requiresCompletedDate;
public bool RequiresExemptionReason => requiresExemptionReasons;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ public static class JourneyNames
public const string CloseAlert = nameof(CloseAlert);
public const string ReopenAlert = nameof(ReopenAlert);
public const string DeleteAlert = nameof(DeleteAlert);
public const string EditInduction = nameof(EditInduction);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@page "/persons/{PersonId}/edit-induction/check-answers"
@model TeachingRecordSystem.SupportUi.Pages.Persons.PersonDetail.EditInduction.CheckYourAnswersModel
@{
ViewBag.Title = "Check your answers";
}

@section BeforeContent {
<govuk-back-link data-testid="back-link" href="@Model.BackLink">Back</govuk-back-link>
}

<h1 data-testid="page-title" class="govuk-heading-l">@ViewBag.Title</h1>

<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds-from-desktop">
<form action="@LinkGenerator.InductionCheckYourAnswers(Model.PersonId, Model.JourneyInstance!.InstanceId)" method="post">
<div class="govuk-button-group">
<govuk-button type="submit" data-testid="continue-button">Confirm induction details</govuk-button>
<govuk-button data-testid="cancel-button" formaction="@LinkGenerator.InductionCheckYourAnswersCancel(Model.PersonId, Model.JourneyInstance!.InstanceId)" class="govuk-button--secondary" type="submit">Cancel and return to record</govuk-button>
</div>
</form>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Microsoft.AspNetCore.Mvc;

namespace TeachingRecordSystem.SupportUi.Pages.Persons.PersonDetail.EditInduction;

[Journey(JourneyNames.EditInduction), RequireJourneyInstance]
public class CheckYourAnswersModel : CommonJourneyPage
{
public CheckYourAnswersModel(TrsLinkGenerator linkGenerator) : base(linkGenerator)
{
}

public string BackLink => PageLink(InductionJourneyPage.ChangeReasons);

public void OnGet()
{
}

public IActionResult OnPost()
{
// TODO - end of journey logic

return Redirect(NextPage()(PersonId, JourneyInstance!.InstanceId));
}

public Func<Guid, JourneyInstanceId, string> NextPage()
{
return (Id, journeyInstanceId) => LinkGenerator.PersonInduction(Id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using TeachingRecordSystem.SupportUi;
using TeachingRecordSystem.SupportUi.Pages.Persons.PersonDetail.EditInduction;

public abstract class CommonJourneyPage : PageModel
{
protected TrsLinkGenerator LinkGenerator { get; set; }
public JourneyInstance<EditInductionState>? JourneyInstance { get; set; }

[FromRoute]
public Guid PersonId { get; set; }

protected CommonJourneyPage(TrsLinkGenerator linkGenerator)
{
LinkGenerator = linkGenerator;
}

public async Task<IActionResult> OnPostCancelAsync()
{
await JourneyInstance!.DeleteAsync();
return Redirect(LinkGenerator.PersonInduction(PersonId));
}

protected string PageLink(InductionJourneyPage? pageName)
{
return pageName switch
{
InductionJourneyPage.Status => LinkGenerator.InductionEditStatus(PersonId, JourneyInstance!.InstanceId),
InductionJourneyPage.CompletedDate => LinkGenerator.InductionEditCompletedDate(PersonId, JourneyInstance!.InstanceId),
InductionJourneyPage.ExemptionReason => LinkGenerator.InductionEditExemptionReason(PersonId, JourneyInstance!.InstanceId),
InductionJourneyPage.StartDate => LinkGenerator.InductionEditStartDate(PersonId, JourneyInstance!.InstanceId),
InductionJourneyPage.ChangeReasons => LinkGenerator.InductionChangeReason(PersonId, JourneyInstance!.InstanceId),
InductionJourneyPage.CheckAnswers => LinkGenerator.InductionCheckYourAnswers(PersonId, JourneyInstance!.InstanceId),
_ => LinkGenerator.PersonInduction(PersonId)
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@page "/persons/{PersonId}/edit-induction/date-completed"
@model TeachingRecordSystem.SupportUi.Pages.Persons.PersonDetail.EditInduction.CompletedDateModel
@{
ViewBag.Title = "Date completed";
}

@section BeforeContent {
<govuk-back-link data-testid="back-link" href="@Model.BackLink">Back</govuk-back-link>
}

<h1 data-testid="page-title" class="govuk-heading-l">@ViewBag.Title</h1>

<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds-from-desktop">
<form data-testid="submit-form" action="@LinkGenerator.InductionEditCompletedDate(Model.PersonId, Model.JourneyInstance!.InstanceId)" method="post">
<div class="govuk-button-group">
<govuk-button type="submit" data-testid="continue-button">Continue</govuk-button>
<govuk-button data-testid="cancel-button" formaction="@LinkGenerator.InductionEditCompletedDateCancel(Model.PersonId, Model.JourneyInstance!.InstanceId)" class="govuk-button--secondary" type="submit">Cancel and return to record</govuk-button>
</div>
</form>
</<div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Microsoft.AspNetCore.Mvc;

namespace TeachingRecordSystem.SupportUi.Pages.Persons.PersonDetail.EditInduction;

[Journey(JourneyNames.EditInduction), ActivatesJourney, RequireJourneyInstance]
public class CompletedDateModel : CommonJourneyPage
{
public InductionJourneyPage NextPage => InductionJourneyPage.ChangeReasons;
public string BackLink
{
// TODO - more logic needed when other routes to completed-date are added
get => PageLink(InductionJourneyPage.StartDate);
}

public CompletedDateModel(TrsLinkGenerator linkGenerator) : base(linkGenerator)
{
}

public void OnGet()
{
}

public async Task<IActionResult> OnPostAsync()
{
await JourneyInstance!.UpdateStateAsync(state =>
{
// TODO - store the completed date
if (state.JourneyStartPage == null)
{
state.JourneyStartPage = InductionJourneyPage.CompletedDate;
}
});

return Redirect(PageLink(NextPage));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using TeachingRecordSystem.Core.DataStore.Postgres;

namespace TeachingRecordSystem.SupportUi.Pages.Persons.PersonDetail.EditInduction;

public class EditInductionState : IRegisterJourney
{
public static JourneyDescriptor Journey => new(
JourneyNames.EditInduction,
typeof(EditInductionState),
requestDataKeys: ["personId"],
appendUniqueKey: true);

public InductionStatus InductionStatus { get; set; }
public DateOnly? StartDate { get; set; }
public DateOnly? CompletedDate { get; set; }
public InductionExemptionReasons? ExemptionReasons { get; set; }
public string? ChangeReason { get; set; }
public InductionJourneyPage? JourneyStartPage { get; set; }

public bool Initialized { get; set; }

public async Task EnsureInitializedAsync(TrsDbContext dbContext, Guid personId, InductionJourneyPage startPage)
{
if (Initialized)
{
return;
}
var person = await dbContext.Persons
.SingleAsync(q => q.PersonId == personId);
InductionStatus = person!.InductionStatus;
if (JourneyStartPage == null)
{
JourneyStartPage = startPage;
}

Initialized = true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@page "/persons/{PersonId}/edit-induction/exemption-reasons"
@model TeachingRecordSystem.SupportUi.Pages.Persons.PersonDetail.EditInduction.ExemptionReasonModel
@{
ViewBag.Title = "Exemption reason";
}

@section BeforeContent {
<govuk-back-link data-testid="back-link" href="@Model.BackLink">Back</govuk-back-link>
}

<h1 data-testid="page-title" class="govuk-heading-l">@ViewBag.Title</h1>

<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds-from-desktop">
<form action="@LinkGenerator.InductionEditExemptionReason(Model.PersonId, Model.JourneyInstance!.InstanceId)" method="post">
<div class="govuk-button-group">
<govuk-button type="submit" data-testid="continue-button">Continue</govuk-button>
<govuk-button data-testid="cancel-button" formaction="@LinkGenerator.InductionEditExemptionReasonCancel(Model.PersonId, Model.JourneyInstance!.InstanceId)" class="govuk-button--secondary" type="submit">Cancel and return to record</govuk-button>
</div>
</form>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Microsoft.AspNetCore.Mvc;

namespace TeachingRecordSystem.SupportUi.Pages.Persons.PersonDetail.EditInduction;

[Journey(JourneyNames.EditInduction), RequireJourneyInstance]
public class ExemptionReasonModel : CommonJourneyPage
{
public InductionJourneyPage NextPage => InductionJourneyPage.ChangeReasons;
public string BackLink
{
// TODO - more logic needed when other routes to exemption reason are added
get => PageLink(InductionJourneyPage.Status);
}

public ExemptionReasonModel(TrsLinkGenerator linkGenerator) : base(linkGenerator)
{
}

public void OnGet()
{
}

public async Task<IActionResult> OnPostAsync()
{
await JourneyInstance!.UpdateStateAsync(state =>
{
// TODO - store the exemption reason
if (state.JourneyStartPage == null)
{
state.JourneyStartPage = InductionJourneyPage.ExemptionReason;
}
});

return Redirect(PageLink(NextPage));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@page "/persons/{PersonId}/edit-induction/change-reason"
@model TeachingRecordSystem.SupportUi.Pages.Persons.PersonDetail.EditInduction.InductionChangeReasonModel
@{
ViewBag.Title = "Change reason";
}

@section BeforeContent {
<govuk-back-link data-testid="back-link" href="@Model.BackLink">Back</govuk-back-link>
}

<h1 data-testid="page-title" class="govuk-heading-l">@ViewBag.Title</h1>

<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds-from-desktop">
<form action="@LinkGenerator.InductionChangeReason(Model.PersonId, Model.JourneyInstance!.InstanceId)" method="post">
<div class="govuk-button-group">
<govuk-button type="submit" data-testid="continue-button">Continue</govuk-button>
<govuk-button data-testid="cancel-button" formaction="@LinkGenerator.InductionChangeReasonCancel(Model.PersonId, Model.JourneyInstance!.InstanceId)" class="govuk-button--secondary" type="submit">Cancel and return to record</govuk-button>
</div>
</form>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using Microsoft.AspNetCore.Mvc;

namespace TeachingRecordSystem.SupportUi.Pages.Persons.PersonDetail.EditInduction;

[Journey(JourneyNames.EditInduction), RequireJourneyInstance]
public class InductionChangeReasonModel : CommonJourneyPage
{
public InductionStatus InductionStatus => JourneyInstance!.State.InductionStatus;
public InductionJourneyPage NextPage => InductionJourneyPage.CheckAnswers;
public string BackLink
{
get
{
return InductionStatus switch
{
_ when InductionStatus.RequiresCompletedDate() => PageLink(InductionJourneyPage.CompletedDate),
_ when InductionStatus.RequiresStartDate() => PageLink(InductionJourneyPage.StartDate),
_ when InductionStatus.RequiresExemptionReasons() => PageLink(InductionJourneyPage.ExemptionReason),
_ => PageLink(InductionJourneyPage.Status),
};
}
}

public InductionChangeReasonModel(TrsLinkGenerator linkGenerator) : base(linkGenerator)
{
}

public void OnGet()
{
}

public async Task<IActionResult> OnPostAsync()
{
await JourneyInstance!.UpdateStateAsync(state =>
{
// TODO - store the change reason
});

return Redirect(PageLink(NextPage));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace TeachingRecordSystem.SupportUi.Pages.Persons.PersonDetail.EditInduction;

public enum InductionJourneyPage
{
Status,
ExemptionReason,
CathLass marked this conversation as resolved.
Show resolved Hide resolved
StartDate,
CompletedDate,
ChangeReasons,
CheckAnswers
}
Loading
Loading