From b1f404777e976f34aa4b282c90b78f8e9704fa08 Mon Sep 17 00:00:00 2001 From: Andrew Horth Date: Tue, 10 Oct 2023 15:19:37 +0100 Subject: [PATCH] Add alert journey (#855) * Started add alert page * Added basic functionality to add alert page * Finished off backend and UI to add an alert * Added CRM and UI tests * Added E2E test for adding an alert * Fixed inconsistency * Fixed issues from PR comments --- .../Dqt/Queries/CreateSanctionQuery.cs | 10 + .../QueryHandlers/CreateSanctionHandler.cs | 23 +++ .../Dqt/ReferenceDataCache.cs | 12 ++ .../JourneyNames.cs | 1 + .../Pages/Alerts/AddAlert/AddAlertState.cs | 19 ++ .../Pages/Alerts/AddAlert/Confirm.cshtml | 41 ++++ .../Pages/Alerts/AddAlert/Confirm.cshtml.cs | 87 +++++++++ .../Pages/Alerts/AddAlert/Index.cshtml | 54 ++++++ .../Pages/Alerts/AddAlert/Index.cshtml.cs | 122 ++++++++++++ .../Pages/Common/AlertType.cs | 8 + .../Persons/PersonDetail/AddAlert.cshtml | 7 - .../Persons/PersonDetail/AddAlert.cshtml.cs | 10 - .../Pages/Persons/PersonDetail/Alerts.cshtml | 2 +- .../TeachingRecordSystem.SupportUi/Program.cs | 6 + .../TrsLinkGenerator.cs | 8 +- .../Components/accessible-autocomplete.min.js | 2 + .../accessible-autocomplete.min.css | 1 + .../wwwroot/Styles/site.scss | 4 + .../QueryTests/CreateSanctionTests.cs | 49 +++++ .../AlertTests.cs | 33 ++++ .../PageExtensions.cs | 26 +++ .../PageTests/Alerts/AddAlert/ConfirmTests.cs | 156 +++++++++++++++ .../PageTests/Alerts/AddAlert/IndexTests.cs | 180 ++++++++++++++++++ 23 files changed, 842 insertions(+), 19 deletions(-) create mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/Queries/CreateSanctionQuery.cs create mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/QueryHandlers/CreateSanctionHandler.cs create mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/AddAlert/AddAlertState.cs create mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/AddAlert/Confirm.cshtml create mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/AddAlert/Confirm.cshtml.cs create mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/AddAlert/Index.cshtml create mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/AddAlert/Index.cshtml.cs create mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Common/AlertType.cs delete mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Persons/PersonDetail/AddAlert.cshtml delete mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Persons/PersonDetail/AddAlert.cshtml.cs create mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/wwwroot/Scripts/Components/accessible-autocomplete.min.js create mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/wwwroot/Styles/Components/accessible-autocomplete.min.css create mode 100644 TeachingRecordSystem/tests/TeachingRecordSystem.Core.Dqt.Tests/QueryTests/CreateSanctionTests.cs create mode 100644 TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.Tests/PageTests/Alerts/AddAlert/ConfirmTests.cs create mode 100644 TeachingRecordSystem/tests/TeachingRecordSystem.SupportUi.Tests/PageTests/Alerts/AddAlert/IndexTests.cs diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/Queries/CreateSanctionQuery.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/Queries/CreateSanctionQuery.cs new file mode 100644 index 000000000..c634c387e --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/Queries/CreateSanctionQuery.cs @@ -0,0 +1,10 @@ +namespace TeachingRecordSystem.Core.Dqt.Queries; + +public record CreateSanctionQuery : ICrmQuery +{ + public required Guid ContactId { get; init; } + public required Guid SanctionCodeId { get; init; } + public required string Details { get; init; } + public required string? Link { get; init; } + public required DateOnly StartDate { get; init; } +} diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/QueryHandlers/CreateSanctionHandler.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/QueryHandlers/CreateSanctionHandler.cs new file mode 100644 index 000000000..50d8d3971 --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/QueryHandlers/CreateSanctionHandler.cs @@ -0,0 +1,23 @@ +using Microsoft.PowerPlatform.Dataverse.Client; +using TeachingRecordSystem.Core.Dqt.Queries; + +namespace TeachingRecordSystem.Core.Dqt.QueryHandlers; + +public class CreateSanctionHandler : ICrmQueryHandler +{ + public async Task Execute(CreateSanctionQuery query, IOrganizationServiceAsync organizationService) + { + var sanction = new dfeta_sanction() + { + Id = Guid.NewGuid(), + dfeta_PersonId = query.ContactId.ToEntityReference(Contact.EntityLogicalName), + dfeta_SanctionCodeId = query.SanctionCodeId.ToEntityReference(dfeta_sanctioncode.EntityLogicalName), + dfeta_SanctionDetails = query.Details, + dfeta_DetailsLink = query.Link, + dfeta_StartDate = query.StartDate.FromDateOnlyWithDqtBstFix(isLocalTime: true) + }; + + var sanctionId = await organizationService.CreateAsync(sanction); + return sanctionId; + } +} diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/ReferenceDataCache.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/ReferenceDataCache.cs index fa7e4f4f4..7bc5c0bba 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/ReferenceDataCache.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/ReferenceDataCache.cs @@ -21,6 +21,18 @@ public async Task GetSanctionCodeByValue(string value) return sanctionCodes.First(s => s.dfeta_Value == value); } + public async Task GetSanctionCodeById(Guid sanctionCodeId) + { + var sanctionCodes = await EnsureSanctionCodes(); + return sanctionCodes.Single(s => s.dfeta_sanctioncodeId == sanctionCodeId); + } + + public async Task GetSanctionCodes() + { + var sanctionCodes = await EnsureSanctionCodes(); + return sanctionCodes.ToArray(); + } + public async Task GetSubjectByTitle(string title) { var subjects = await EnsureSubjects(); diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/JourneyNames.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/JourneyNames.cs index c76918ffb..7471ed7ba 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/JourneyNames.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/JourneyNames.cs @@ -2,5 +2,6 @@ namespace TeachingRecordSystem.SupportUi; public static class JourneyNames { + public const string AddAlert = nameof(AddAlert); public const string CloseAlert = nameof(CloseAlert); } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/AddAlert/AddAlertState.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/AddAlert/AddAlertState.cs new file mode 100644 index 000000000..24119a84e --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/AddAlert/AddAlertState.cs @@ -0,0 +1,19 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace TeachingRecordSystem.SupportUi.Pages.Alerts.AddAlert; + +public class AddAlertState +{ + public Guid? AlertTypeId { get; set; } + + public string? Details { get; set; } + + public string? Link { get; set; } + + public DateOnly? StartDate { get; set; } + + [JsonIgnore] + [MemberNotNullWhen(true, nameof(AlertTypeId), nameof(Details), nameof(StartDate))] + public bool IsComplete => AlertTypeId.HasValue && !string.IsNullOrWhiteSpace(Details) && StartDate.HasValue; +} diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/AddAlert/Confirm.cshtml b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/AddAlert/Confirm.cshtml new file mode 100644 index 000000000..628d82e32 --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/AddAlert/Confirm.cshtml @@ -0,0 +1,41 @@ +@page "/alerts/add/confirm" +@model TeachingRecordSystem.SupportUi.Pages.Alerts.AddAlert.ConfirmModel +@{ + ViewBag.Title = "Confirm alert information"; +} + +@section BeforeContent { + Back +} + +

@ViewBag.Title

+ +
+
+
+ + + Alert type + @Model.AlertType + + + Details + + + @if (!string.IsNullOrWhiteSpace(Model.Link)) + { + + Link + @Model.Link + + } + + Start date + @Model.StartDate!.Value.ToString("dd/MM/yyyy") + + + + Confirm +
+
+
diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/AddAlert/Confirm.cshtml.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/AddAlert/Confirm.cshtml.cs new file mode 100644 index 000000000..0637e10ab --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/AddAlert/Confirm.cshtml.cs @@ -0,0 +1,87 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Xrm.Sdk.Query; +using TeachingRecordSystem.Core.Dqt.Models; +using TeachingRecordSystem.Core.Dqt.Queries; + +namespace TeachingRecordSystem.SupportUi.Pages.Alerts.AddAlert; + +[Journey(JourneyNames.AddAlert), RequireJourneyInstance] +public class ConfirmModel : PageModel +{ + private readonly TrsLinkGenerator _linkGenerator; + private readonly ICrmQueryDispatcher _crmQueryDispatcher; + private readonly ReferenceDataCache _referenceDataCache; + + public ConfirmModel( + TrsLinkGenerator linkGenerator, + ICrmQueryDispatcher crmQueryDispatcher, + ReferenceDataCache referenceDataCache) + { + _linkGenerator = linkGenerator; + _crmQueryDispatcher = crmQueryDispatcher; + _referenceDataCache = referenceDataCache; + } + + public JourneyInstance? JourneyInstance { get; set; } + + [FromQuery] + public Guid PersonId { get; set; } + + public Guid? AlertTypeId { get; set; } + + public string? AlertType { get; set; } + + public string? Details { get; set; } + + public string? Link { get; set; } + + public DateOnly? StartDate { get; set; } + + public async Task OnPost() + { + await _crmQueryDispatcher.ExecuteQuery( + new CreateSanctionQuery() + { + ContactId = PersonId, + SanctionCodeId = AlertTypeId!.Value, + Details = Details!, + Link = Link, + StartDate = StartDate!.Value + }); + + await JourneyInstance!.CompleteAsync(); + + TempData.SetFlashSuccess("Alert added"); + + return Redirect(_linkGenerator.PersonAlerts(PersonId)); + } + + public override async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) + { + var person = await _crmQueryDispatcher.ExecuteQuery(new GetContactDetailByIdQuery(PersonId, new ColumnSet(Contact.Fields.Id))); + if (person is null) + { + context.Result = NotFound(); + return; + } + + if (!JourneyInstance!.State.IsComplete) + { + context.Result = Redirect(_linkGenerator.AlertAdd(PersonId, JourneyInstance!.InstanceId)); + return; + } + + var sanctionCodeId = JourneyInstance!.State.AlertTypeId!.Value; + var sanctionCode = await _referenceDataCache.GetSanctionCodeById(sanctionCodeId); + + AlertTypeId = sanctionCodeId; + AlertType = sanctionCode.dfeta_name; + Details = JourneyInstance!.State.Details; + Link = JourneyInstance!.State.Link; + StartDate = JourneyInstance!.State.StartDate; + + await next(); + } +} diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/AddAlert/Index.cshtml b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/AddAlert/Index.cshtml new file mode 100644 index 000000000..b1922789e --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/AddAlert/Index.cshtml @@ -0,0 +1,54 @@ +@page "/alerts/add" +@model IndexModel +@addTagHelper *, Joonasw.AspNetCore.SecurityHeaders +@{ + ViewBag.Title = "Add an alert"; +} + +@section Styles { + +} + +@section Scripts { + + +} + +@section BeforeContent { + Back +} + +

@ViewBag.Title

+ +
+
+
+ + + @foreach (var alertType in Model.AlertTypes!) + { + @alertType.Name + } + + + + + + + + + + + + + Continue + +
+
+ diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/AddAlert/Index.cshtml.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/AddAlert/Index.cshtml.cs new file mode 100644 index 000000000..6189a11cd --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Alerts/AddAlert/Index.cshtml.cs @@ -0,0 +1,122 @@ +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Xrm.Sdk.Query; +using TeachingRecordSystem.Core.Dqt.Models; +using TeachingRecordSystem.Core.Dqt.Queries; +using TeachingRecordSystem.SupportUi.Pages.Common; + +namespace TeachingRecordSystem.SupportUi.Pages.Alerts.AddAlert; + +[Journey(JourneyNames.AddAlert), ActivatesJourney, RequireJourneyInstance] +public class IndexModel : PageModel +{ + private readonly TrsLinkGenerator _linkGenerator; + private readonly ICrmQueryDispatcher _crmQueryDispatcher; + private readonly ReferenceDataCache _referenceDataCache; + + public IndexModel( + TrsLinkGenerator linkGenerator, + ICrmQueryDispatcher crmQueryDispatcher, + ReferenceDataCache referenceDataCache) + { + _linkGenerator = linkGenerator; + _crmQueryDispatcher = crmQueryDispatcher; + _referenceDataCache = referenceDataCache; + } + + public JourneyInstance? JourneyInstance { get; set; } + + [FromQuery] + public Guid PersonId { get; set; } + + [BindProperty] + [Display(Name = "Alert type")] + public Guid? AlertTypeId { get; set; } + + [BindProperty] + [Display(Description = "You can enter up to 4000 characters")] + public string? Details { get; set; } + + [BindProperty] + [Display(Description = "Optional")] + public string? Link { get; set; } + + [BindProperty] + [Display(Name = "Start date")] + public DateOnly? StartDate { get; set; } + + public AlertType[]? AlertTypes { get; set; } + + public async Task OnPost() + { + if (AlertTypeId is null) + { + ModelState.AddModelError(nameof(AlertTypeId), "Add an alert type"); + } + + if (string.IsNullOrWhiteSpace(Details)) + { + ModelState.AddModelError(nameof(Details), "Add details"); + } + + if (!string.IsNullOrEmpty(Link) && + (!Uri.TryCreate(Link, UriKind.Absolute, out var uri) || + (uri.Scheme != "http" && uri.Scheme != "https"))) + { + ModelState.AddModelError(nameof(Link), "Enter a valid URL"); + } + + if (StartDate is null) + { + ModelState.AddModelError(nameof(StartDate), "Add a start date"); + } + + if (!ModelState.IsValid) + { + return this.PageWithErrors(); + } + + await JourneyInstance!.UpdateStateAsync(s => + { + s.AlertTypeId = AlertTypeId; + s.Details = Details; + s.Link = Link; + s.StartDate = StartDate; + }); + + return Redirect(_linkGenerator.AlertAddConfirm(PersonId, JourneyInstance!.InstanceId)); + } + + public override async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) + { + var person = await _crmQueryDispatcher.ExecuteQuery(new GetContactDetailByIdQuery(PersonId, new ColumnSet(Contact.Fields.Id))); + if (person is null) + { + context.Result = NotFound(); + return; + } + + var sanctionCodes = await _referenceDataCache.GetSanctionCodes(); + AlertTypes = sanctionCodes + .Select(MapSanctionCode) + .OrderBy(a => a.Name) + .ToArray(); + + AlertTypeId ??= JourneyInstance!.State.AlertTypeId; + Details ??= JourneyInstance!.State.Details; + Link ??= JourneyInstance!.State.Link; + StartDate ??= JourneyInstance!.State.StartDate; + + await next(); + } + + private AlertType MapSanctionCode(dfeta_sanctioncode sanctionCode) => + new AlertType() + { + AlertTypeId = sanctionCode.dfeta_sanctioncodeId!.Value, + Value = sanctionCode.dfeta_Value, + Name = sanctionCode.dfeta_name + }; +} diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Common/AlertType.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Common/AlertType.cs new file mode 100644 index 000000000..90f0a8437 --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Common/AlertType.cs @@ -0,0 +1,8 @@ +namespace TeachingRecordSystem.SupportUi.Pages.Common; + +public record AlertType +{ + public required Guid AlertTypeId { get; init; } + public required string Name { get; init; } + public required string Value { get; init; } +} diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Persons/PersonDetail/AddAlert.cshtml b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Persons/PersonDetail/AddAlert.cshtml deleted file mode 100644 index e74afd506..000000000 --- a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Persons/PersonDetail/AddAlert.cshtml +++ /dev/null @@ -1,7 +0,0 @@ -@page "/persons/{personId}/alerts/new" -@model TeachingRecordSystem.SupportUi.Pages.Persons.PersonDetail.AddAlertModel -@{ - ViewBag.Title = "Add alert"; -} - -

@ViewBag.Title

diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Persons/PersonDetail/AddAlert.cshtml.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Persons/PersonDetail/AddAlert.cshtml.cs deleted file mode 100644 index 071843f2d..000000000 --- a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Persons/PersonDetail/AddAlert.cshtml.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace TeachingRecordSystem.SupportUi.Pages.Persons.PersonDetail; - -public class AddAlertModel : PageModel -{ - public void OnGet() - { - } -} diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Persons/PersonDetail/Alerts.cshtml b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Persons/PersonDetail/Alerts.cshtml index f1afda793..0ae49364a 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Persons/PersonDetail/Alerts.cshtml +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Pages/Persons/PersonDetail/Alerts.cshtml @@ -45,7 +45,7 @@ else } } -Add an alert +Add an alert @if (Model.PreviousAlerts is not null && Model.PreviousAlerts.Length > 0) { diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Program.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Program.cs index 7c5a7f558..bf0300c77 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Program.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/Program.cs @@ -187,6 +187,12 @@ .AddTransient() .AddFormFlow(options => { + options.JourneyRegistry.RegisterJourney(new JourneyDescriptor( + JourneyNames.AddAlert, + typeof(TeachingRecordSystem.SupportUi.Pages.Alerts.AddAlert.AddAlertState), + requestDataKeys: new[] { "personId" }, + appendUniqueKey: true)); + options.JourneyRegistry.RegisterJourney(new JourneyDescriptor( JourneyNames.CloseAlert, typeof(TeachingRecordSystem.SupportUi.Pages.Alerts.CloseAlert.CloseAlertState), diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/TrsLinkGenerator.cs b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/TrsLinkGenerator.cs index 71fe5f1d8..84921b5b4 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/TrsLinkGenerator.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/TrsLinkGenerator.cs @@ -23,6 +23,12 @@ public TrsLinkGenerator(LinkGenerator linkGenerator) public string Alert(Guid alertId) => GetRequiredPathByPage("/Alerts/Alert/Index", routeValues: new { alertId }); + public string AlertAdd(Guid personId, JourneyInstanceId? journeyInstanceId) => + GetRequiredPathByPage("/Alerts/AddAlert/Index", routeValues: new { personId }, journeyInstanceId: journeyInstanceId); + + public string AlertAddConfirm(Guid personId, JourneyInstanceId journeyInstanceId) => + GetRequiredPathByPage("/Alerts/AddAlert/Confirm", routeValues: new { personId }, journeyInstanceId: journeyInstanceId); + public string AlertClose(Guid alertId, JourneyInstanceId? journeyInstanceId) => GetRequiredPathByPage("/Alerts/CloseAlert/Index", routeValues: new { alertId }, journeyInstanceId: journeyInstanceId); @@ -48,7 +54,7 @@ public string PersonDetail(Guid personId, string? search = null, ContactSearchSo public string PersonAlerts(Guid personId, string? search = null, ContactSearchSortByOption? sortBy = null, int? pageNumber = null) => GetRequiredPathByPage("/Persons/PersonDetail/Alerts", routeValues: new { personId, search, sortBy, pageNumber }); - public string PersonAddAlert(Guid personId) => GetRequiredPathByPage("/Persons/PersonDetail/AddAlert", routeValues: new { personId }); + public string PersonAddAlert(Guid personId) => GetRequiredPathByPage("/Alerts/AddAlert/Index", routeValues: new { personId }); public string PersonChangeLog(Guid personId, string? search = null, ContactSearchSortByOption? sortBy = null, int? pageNumber = null) => GetRequiredPathByPage("/Persons/PersonDetail/ChangeLog", routeValues: new { personId, search, sortBy, pageNumber }); diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/wwwroot/Scripts/Components/accessible-autocomplete.min.js b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/wwwroot/Scripts/Components/accessible-autocomplete.min.js new file mode 100644 index 000000000..adc638d7f --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.SupportUi/wwwroot/Scripts/Components/accessible-autocomplete.min.js @@ -0,0 +1,2 @@ +(function webpackUniversalModuleDefinition(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports["accessibleAutocomplete"]=t():e["accessibleAutocomplete"]=t()})(window,function(){return function(n){var r={};function o(e){if(r[e])return r[e].exports;var t=r[e]={i:e,l:!1,exports:{}};return n[e].call(t.exports,t,t.exports,o),t.l=!0,t.exports}return o.m=n,o.c=r,o.d=function(e,t,n){o.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},o.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.t=function(t,e){if(1&e&&(t=o(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(o.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)o.d(n,r,function(e){return t[e]}.bind(null,r));return n},o.n=function(e){var t=e&&e.__esModule?function(){return e["default"]}:function(){return e};return o.d(t,"a",t),t},o.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},o.p="/",o(o.s=37)}([function(e,t,n){var m=n(1),v=n(6),y=n(7),g=n(16),_=n(18),b="prototype",w=function(e,t,n){var r,o,i,u,a=e&w.F,s=e&w.G,l=e&w.S,c=e&w.P,p=e&w.B,f=s?m:l?m[t]||(m[t]={}):(m[t]||{})[b],d=s?v:v[t]||(v[t]={}),h=d[b]||(d[b]={});for(r in s&&(n=t),n)i=((o=!a&&f&&f[r]!==undefined)?f:n)[r],u=p&&o?_(i,m):c&&"function"==typeof i?_(Function.call,i):i,f&&g(f,r,i,e&w.U),d[r]!=i&&y(d,r,u),c&&h[r]!=i&&(h[r]=i)};m.core=v,w.F=1,w.G=2,w.S=4,w.P=8,w.B=16,w.W=32,w.U=64,w.R=128,e.exports=w},function(e,t){var n=e.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},function(e,t){e.exports=function(e){return"object"==typeof e?null!==e:"function"==typeof e}},function(e,t,n){e.exports=!n(4)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(e,t){e.exports=function(e){try{return!!e()}catch(t){return!0}}},function(e,t,n){"use strict";n.r(t),n.d(t,"h",function(){return r}),n.d(t,"createElement",function(){return r}),n.d(t,"cloneElement",function(){return i}),n.d(t,"Component",function(){return g}),n.d(t,"render",function(){return _}),n.d(t,"rerender",function(){return f}),n.d(t,"options",function(){return E});var s=function s(){},E={},l=[],c=[];function r(e,t){var n,r,o,i,u=c;for(i=arguments.length;2o;)u(r,n=t[o++])&&(~s(i,n)||i.push(n));return i}},function(e,t,n){var r=n(25),o=Math.max,i=Math.min;e.exports=function(e,t){return(e=r(e))<0?o(e+t,0):i(e,t)}},function(e,t){e.exports=!1},function(e,t){t.f=Object.getOwnPropertySymbols},function(e,t){t.f={}.propertyIsEnumerable},function(e,t,n){var r=n(48);e.exports=function(e,t){return new(r(e))(t)}},function(e,t,n){var r=n(2),o=n(33),i=n(34)("species");e.exports=function(e){var t;return o(e)&&("function"!=typeof(t=e.constructor)||t!==Array&&!o(t.prototype)||(t=undefined),r(t)&&null===(t=t[i])&&(t=undefined)),t===undefined?Array:t}},function(e,t,n){"use strict";var r=n(0),o=n(32)(2);r(r.P+r.F*!n(13)([].filter,!0),"Array",{filter:function(e){return o(this,e,arguments[1])}})},function(e,t,n){var r=n(0);r(r.S,"Array",{isArray:n(33)})},function(e,t,n){"use strict";t.__esModule=!0,t["default"]=void 0,n(14),n(36),n(30),n(31),n(35),n(55),n(58);var J=n(5),X=o(n(60)),r=o(n(61));function o(e){return e&&e.__esModule?e:{"default":e}}function Y(){return(Y=Object.assign||function(e){for(var t=1;t=r;this.setState({query:a,ariaHint:s}),i||!s&&l&&c?o(a,function(e){var t=0=i&&0=o,j=this.props.showNoOptionsFound&&A&&I&&k&&P,L=n+"__wrapper",T=n+"__input",B=null!==b?" "+T+"--focused":"",D=this.props.showAllValues?" "+T+"--show-all-values":" "+T+"--default",F=n+"__dropdown-arrow-down",R=-1!==b&&null!==b,U=n+"__menu",V=U+"--"+r,q=U+"--"+(x||j?"visible":"hidden"),W=n+"__option",H=n+"__hint",K=this.templateInputValue(O[S]),Q=K&&0===K.toLowerCase().indexOf(C.toLowerCase())&&N?C+K.substr(C.length):"",z=u+"__assistiveHint",G=E?{"aria-describedby":z}:null;return c&&"string"==typeof(e=y({className:F}))&&(e=(0,J.createElement)("div",{className:n+"__dropdown-arrow-down-wrapper",dangerouslySetInnerHTML:{__html:e}})),(0,J.createElement)("div",{className:L,onKeyDown:this.handleKeyDown},(0,J.createElement)(X["default"],{id:u,length:O.length,queryLength:C.length,minQueryLength:o,selectedOption:this.templateInputValue(O[S]),selectedOptionIndex:S,validChoiceMade:M,isInFocus:null!==this.state.focused,tQueryTooShort:f,tNoResults:d,tSelectedOption:h,tResults:m}),Q&&(0,J.createElement)("span",null,(0,J.createElement)("input",{className:H,readonly:!0,tabIndex:"-1",value:Q})),(0,J.createElement)("input",Y({"aria-expanded":x?"true":"false","aria-activedescendant":!!R&&u+"__option--"+b,"aria-owns":u+"__listbox","aria-autocomplete":this.hasAutoselect()?"both":"list"},G,{autoComplete:"off",className:""+T+B+D,id:u,onClick:function(e){return i.handleInputClick(e)},onBlur:this.handleInputBlur},function $(e){return{onInput:e}}(this.handleInputChange),{onFocus:this.handleInputFocus,name:a,placeholder:s,ref:function(e){i.elementReferences[-1]=e},type:"text",role:"combobox",required:l,value:C})),e,(0,J.createElement)("ul",Y({className:U+" "+V+" "+q,onMouseLeave:function(e){return i.handleListMouseLeave(e)},id:u+"__listbox",role:"listbox"},g),O.map(function(e,t){var n=(-1===b?S===t:b===t)&&null===w?" "+W+"--focused":"",r=t%2?" "+W+"--odd":"",o=Z()?" '+(t+1)+" of "+O.length+"":"";return(0,J.createElement)("li",{"aria-selected":b===t?"true":"false",className:""+W+n+r,dangerouslySetInnerHTML:{__html:i.templateSuggestion(e)+o},id:u+"__option--"+t,key:t,onBlur:function(e){return i.handleOptionBlur(e,t)},onClick:function(e){return i.handleOptionClick(e,t)},onMouseDown:i.handleOptionMouseDown,onMouseEnter:function(e){return i.handleOptionMouseEnter(e,t)},ref:function(e){i.elementReferences[t]=e},role:"option",tabIndex:"-1","aria-posinset":t+1,"aria-setsize":O.length})}),j&&(0,J.createElement)("li",{className:W+" "+W+"--no-results"},p())),(0,J.createElement)("span",{id:z,style:{display:"none"}},v()))},e}(J.Component);(t["default"]=a).defaultProps={autoselect:!1,cssNamespace:"autocomplete",defaultValue:"",displayMenu:"inline",minLength:0,name:"input-autocomplete",placeholder:"",onConfirm:function(){},confirmOnBlur:!0,showNoOptionsFound:!0,showAllValues:!1,required:!1,tNoResults:function(){return"No results found"},tAssistiveHint:function(){return"When autocomplete results are available use up and down arrows to review and enter to select. Touch device users, explore by touch or with swipe gestures."},dropdownArrow:r["default"],menuAttributes:{}}},function(e,t,r){var o=r(9),i=r(53),u=r(28),a=r(26)("IE_PROTO"),s=function(){},l="prototype",c=function(){var e,t=r(15)("iframe"),n=u.length;for(t.style.display="none",r(54).appendChild(t),t.src="javascript:",(e=t.contentWindow.document).open(),e.write("