diff --git a/AdminWebsite/AdminWebsite.UnitTests/Controllers/BookingListControllerTest.cs b/AdminWebsite/AdminWebsite.UnitTests/Controllers/BookingListControllerTest.cs index 1fbd9278c..8c28003a1 100644 --- a/AdminWebsite/AdminWebsite.UnitTests/Controllers/BookingListControllerTest.cs +++ b/AdminWebsite/AdminWebsite.UnitTests/Controllers/BookingListControllerTest.cs @@ -452,7 +452,7 @@ public async Task Should_return_booking_list_when_admin_search_by_multiple_crite okResult.StatusCode.Should().Be(200); } - private List GetCaseTypesList() + private static List GetCaseTypesList() { return new List { diff --git a/AdminWebsite/AdminWebsite.UnitTests/Controllers/HearingsController/PostHearingTests.cs b/AdminWebsite/AdminWebsite.UnitTests/Controllers/HearingsController/PostHearingTests.cs index 42dfd063d..287fa1f43 100644 --- a/AdminWebsite/AdminWebsite.UnitTests/Controllers/HearingsController/PostHearingTests.cs +++ b/AdminWebsite/AdminWebsite.UnitTests/Controllers/HearingsController/PostHearingTests.cs @@ -1,21 +1,16 @@ using AdminWebsite.Models; -using AdminWebsite.Security; using AdminWebsite.Services; using AdminWebsite.UnitTests.Helper; -using FizzWare.NBuilder; using Microsoft.AspNetCore.Mvc; using System.Linq; using System.Net; using System.Threading.Tasks; -using AdminWebsite.Configuration; -using AdminWebsite.Contracts.Enums; using AdminWebsite.Contracts.Requests; using BookingsApi.Client; using Autofac.Extras.Moq; using BookingsApi.Contract.V1.Requests; +using BookingsApi.Contract.V2.Requests; using VideoApi.Contract.Responses; -using EndpointRequest = AdminWebsite.Contracts.Requests.EndpointRequest; -using LinkedParticipantRequest = AdminWebsite.Contracts.Requests.LinkedParticipantRequest; using ParticipantRequest = AdminWebsite.Contracts.Requests.ParticipantRequest; using V1 = BookingsApi.Contract.V1; @@ -69,7 +64,7 @@ public void Should_throw_BookingsApiException() BookingDetails = hearing }; - _mocker.Mock().Setup(x => x.BookNewHearingAsync(It.IsAny())) + _mocker.Mock().Setup(x => x.BookNewHearingWithCodeAsync(It.IsAny())) .Throws(ClientException.ForBookingsAPI(HttpStatusCode.InternalServerError)); var response = _controller.Post(bookingRequest); @@ -90,7 +85,7 @@ public void Should_throw_Exception() BookingDetails = hearing }; - _mocker.Mock().Setup(x => x.BookNewHearingAsync(It.IsAny())) + _mocker.Mock().Setup(x => x.BookNewHearingWithCodeAsync(It.IsAny())) .Throws(new Exception("Some internal error")); var response = _controller.Post(bookingRequest); diff --git a/AdminWebsite/AdminWebsite.UnitTests/Services/HearingServiceTests.cs b/AdminWebsite/AdminWebsite.UnitTests/Services/HearingServiceTests.cs index 8ea6db4b9..b70709cfd 100644 --- a/AdminWebsite/AdminWebsite.UnitTests/Services/HearingServiceTests.cs +++ b/AdminWebsite/AdminWebsite.UnitTests/Services/HearingServiceTests.cs @@ -116,7 +116,7 @@ public async Task Should_process_participants() && x.LinkedParticipants == linkedParticipants)), Times.Once); } - private HearingDetailsResponse InitHearing() + private static HearingDetailsResponse InitHearing() { var cases = new List { new CaseResponse { Name = "Test", Number = "123456" } }; var rep = Builder.CreateNew() diff --git a/AdminWebsite/AdminWebsite/AdminWebsite.csproj b/AdminWebsite/AdminWebsite/AdminWebsite.csproj index aa093685d..5d0b0a5b1 100644 --- a/AdminWebsite/AdminWebsite/AdminWebsite.csproj +++ b/AdminWebsite/AdminWebsite/AdminWebsite.csproj @@ -87,6 +87,7 @@ + diff --git a/AdminWebsite/AdminWebsite/Attributes/HearingInputSanitizerAttribute.cs b/AdminWebsite/AdminWebsite/Attributes/HearingInputSanitizerAttribute.cs index addf9858c..63d2c5824 100644 --- a/AdminWebsite/AdminWebsite/Attributes/HearingInputSanitizerAttribute.cs +++ b/AdminWebsite/AdminWebsite/Attributes/HearingInputSanitizerAttribute.cs @@ -7,13 +7,13 @@ namespace AdminWebsite.Attributes { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] - public class HearingInputSanitizerAttribute : ActionFilterAttribute + public partial class HearingInputSanitizerAttribute : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext context) { - if (context.ActionArguments != null && context.ActionArguments.ContainsKey("request")) + if (context.ActionArguments.TryGetValue("request", out var argument)) { - switch (context.ActionArguments["request"]) + switch (argument) { case BookNewHearingRequest newHearingRequest: newHearingRequest.HearingRoomName = Sanitize(newHearingRequest.HearingRoomName); @@ -74,9 +74,12 @@ private static string Sanitize(string input) return input; } - var regex = new Regex(@"<(.*?)>", RegexOptions.Compiled); + var regex = InputRegex(); return regex.Replace(input, string.Empty); } + + [GeneratedRegex(@"<(.*?)>", RegexOptions.Compiled)] + private static partial Regex InputRegex(); } } \ No newline at end of file diff --git a/AdminWebsite/AdminWebsite/ClientApp/acessability_lint.js b/AdminWebsite/AdminWebsite/ClientApp/acessability_lint.js index 8c67d4bbb..4c098c52a 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/acessability_lint.js +++ b/AdminWebsite/AdminWebsite/ClientApp/acessability_lint.js @@ -32,26 +32,28 @@ async function runPa11y(filename) { } const run = () => { - return new Promise(async (resolve, reject) => { - try { - const result = []; - const files = await getHtmlFiles(); - output(`detected ${files.length} html files to parse`); - for (let index = 0; index < files.length; index += 1) { - const lintErrors = await runPa11y(files[index]); - if (lintErrors.issues.length > 0) { - result.push({ - file: lintErrors.pageUrl, - issues: lintErrors.issues - }); + return new Promise((resolve, reject) => { + (async () => { + try { + const result = []; + const files = await getHtmlFiles(); + output(`detected ${files.length} html files to parse`); + for (let index = 0; index < files.length; index += 1) { + const lintErrors = await runPa11y(files[index]); + if (lintErrors.issues.length > 0) { + result.push({ + file: lintErrors.pageUrl, + issues: lintErrors.issues + }); + } + const doneDegree = Math.round((index / files.length) * 100); + output(`${doneDegree}% done, completed ${files[index]}`); } - const doneDegree = Math.round((index / files.length) * 100); - output(`${doneDegree}% done, completed ${files[index]}`); + resolve(result); + } catch (err) { + reject(err); } - resolve(result); - } catch (err) { - reject(err); - } + })(); }); }; diff --git a/AdminWebsite/AdminWebsite/ClientApp/package-lock.json b/AdminWebsite/AdminWebsite/ClientApp/package-lock.json index 4f202ffb1..7d50f2652 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/package-lock.json +++ b/AdminWebsite/AdminWebsite/ClientApp/package-lock.json @@ -74,7 +74,7 @@ "nswag": "^13.19.0", "pa11y": "^6.2.3", "prettier": "^2.8.8", - "puppeteer": "^22.8.1", + "puppeteer": "^23.6.0", "run-script-os": "^1.1.6", "sass": "^1.32.12", "typescript": "^5.4.4", @@ -4725,19 +4725,20 @@ } }, "node_modules/@puppeteer/browsers": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.2.3.tgz", - "integrity": "sha512-bJ0UBsk0ESOs6RFcLXOt99a3yTDcOKlzfjad+rhFwdaG1Lu/Wzq58GHYCDTlZ9z6mldf4g+NTb+TXEfe0PpnsQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.4.0.tgz", + "integrity": "sha512-x8J1csfIygOwf6D6qUAZ0ASk3z63zPb7wkNeHRerCMh82qWKUrOgkuP005AJC8lDL6/evtXETGEJVcwykKT4/g==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "debug": "4.3.4", - "extract-zip": "2.0.1", - "progress": "2.0.3", - "proxy-agent": "6.4.0", - "semver": "7.6.0", - "tar-fs": "3.0.5", - "unbzip2-stream": "1.4.3", - "yargs": "17.7.2" + "debug": "^4.3.6", + "extract-zip": "^2.0.1", + "progress": "^2.0.3", + "proxy-agent": "^6.4.0", + "semver": "^7.6.3", + "tar-fs": "^3.0.6", + "unbzip2-stream": "^1.4.3", + "yargs": "^17.7.2" }, "bin": { "browsers": "lib/cjs/main-cli.js" @@ -4746,6 +4747,19 @@ "node": ">=18" } }, + "node_modules/@puppeteer/browsers/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.14.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.1.tgz", @@ -5045,7 +5059,8 @@ "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@tufjs/canonical-json": { "version": "2.0.0", @@ -6508,6 +6523,7 @@ "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", "dev": true, + "license": "MIT", "dependencies": { "tslib": "^2.0.1" }, @@ -6609,10 +6625,11 @@ } }, "node_modules/b4a": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", - "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==", - "dev": true + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", + "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", + "dev": true, + "license": "Apache-2.0" }, "node_modules/babel-loader": { "version": "9.1.3", @@ -6734,49 +6751,54 @@ "dev": true }, "node_modules/bare-events": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.2.2.tgz", - "integrity": "sha512-h7z00dWdG0PYOQEvChhOSWvOfkIKsdZGkWr083FgN/HyoQuebSew/cgirYqh9SCuy/hRvxc5Vy6Fw8xAmYHLkQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.0.tgz", + "integrity": "sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A==", "dev": true, + "license": "Apache-2.0", "optional": true }, "node_modules/bare-fs": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.0.tgz", - "integrity": "sha512-TNFqa1B4N99pds2a5NYHR15o0ZpdNKbAeKTE/+G6ED/UeOavv8RY3dr/Fu99HW3zU3pXpo2kDNO8Sjsm2esfOw==", + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.5.tgz", + "integrity": "sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==", "dev": true, + "license": "Apache-2.0", "optional": true, "dependencies": { "bare-events": "^2.0.0", "bare-path": "^2.0.0", - "bare-stream": "^1.0.0" + "bare-stream": "^2.0.0" } }, "node_modules/bare-os": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.3.0.tgz", - "integrity": "sha512-oPb8oMM1xZbhRQBngTgpcQ5gXw6kjOaRsSWsIeNyRxGed2w/ARyP7ScBYpWR1qfX2E5rS3gBw6OWcSQo+s+kUg==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.4.tgz", + "integrity": "sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==", "dev": true, + "license": "Apache-2.0", "optional": true }, "node_modules/bare-path": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.2.tgz", - "integrity": "sha512-o7KSt4prEphWUHa3QUwCxUI00R86VdjiuxmJK0iNVDHYPGo+HsDaVCnqCmPbf/MiW1ok8F4p3m8RTHlWk8K2ig==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz", + "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", "dev": true, + "license": "Apache-2.0", "optional": true, "dependencies": { "bare-os": "^2.1.0" } }, "node_modules/bare-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-1.0.0.tgz", - "integrity": "sha512-KhNUoDL40iP4gFaLSsoGE479t0jHijfYdIcxRn/XtezA2BaUD0NRf/JGRpsMq6dMNM+SrCrB0YSSo/5wBY4rOQ==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.3.2.tgz", + "integrity": "sha512-EFZHSIBkDgSHIwj2l2QZfP4U5OcD4xFAOwhSb/vlr9PIqyGJGvB/nfClJbcnh3EY4jtPE4zsb5ztae96bVF79A==", "dev": true, + "license": "Apache-2.0", "optional": true, "dependencies": { - "streamx": "^2.16.1" + "streamx": "^2.20.0" } }, "node_modules/base64-js": { @@ -6812,6 +6834,7 @@ "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.0.0" } @@ -7224,14 +7247,15 @@ } }, "node_modules/chromium-bidi": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.19.tgz", - "integrity": "sha512-UA6zL77b7RYCjJkZBsZ0wlvCTD+jTjllZ8f6wdO4buevXgTZYjV+XLB9CiEa2OuuTGGTLnI7eN9I60YxuALGQg==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.8.0.tgz", + "integrity": "sha512-uJydbGdTw0DEUjhoogGveneJVWX/9YuqkWePzMmkBYwtdAqo5d3J/ovNKFr+/2hWXYmYCr6it8mSSTIj6SS6Ug==", "dev": true, + "license": "Apache-2.0", "dependencies": { "mitt": "3.0.1", "urlpattern-polyfill": "10.0.0", - "zod": "3.22.4" + "zod": "3.23.8" }, "peerDependencies": { "devtools-protocol": "*" @@ -7914,6 +7938,7 @@ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 14" } @@ -7979,11 +8004,12 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -8072,6 +8098,7 @@ "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", "dev": true, + "license": "MIT", "dependencies": { "ast-types": "^0.13.4", "escodegen": "^2.1.0", @@ -8125,10 +8152,11 @@ "dev": true }, "node_modules/devtools-protocol": { - "version": "0.0.1273771", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1273771.tgz", - "integrity": "sha512-QDbb27xcTVReQQW/GHJsdQqGKwYBE7re7gxehj467kKP2DKuYBUj6i2k5LRiAC66J1yZG/9gsxooz/s9pcm0Og==", - "dev": true + "version": "0.0.1354347", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1354347.tgz", + "integrity": "sha512-BlmkSqV0V84E2WnEnoPnwyix57rQxAM5SKJjf4TbYOCGLAWtz8CDH8RIaGOjPgPCXo2Mce3kxSY497OySidY3Q==", + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/di": { "version": "0.0.1", @@ -8728,6 +8756,7 @@ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", @@ -8749,6 +8778,7 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, + "license": "BSD-3-Clause", "optional": true, "engines": { "node": ">=0.10.0" @@ -9524,7 +9554,8 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-glob": { "version": "3.3.2", @@ -10020,6 +10051,7 @@ "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", "dev": true, + "license": "MIT", "dependencies": { "basic-ftp": "^5.0.2", "data-uri-to-buffer": "^6.0.2", @@ -10703,6 +10735,7 @@ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", "dev": true, + "license": "MIT", "dependencies": { "jsbn": "1.1.0", "sprintf-js": "^1.1.3" @@ -10715,7 +10748,8 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/ipaddr.js": { "version": "2.1.0", @@ -11555,7 +11589,8 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/jsesc": { "version": "2.5.2", @@ -12641,7 +12676,8 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/mkdirp": { "version": "0.5.6", @@ -12679,9 +12715,10 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/multicast-dns": { "version": "7.2.5", @@ -12788,6 +12825,7 @@ "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4.0" } @@ -13984,19 +14022,34 @@ "dev": true }, "node_modules/pac-proxy-agent": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz", - "integrity": "sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz", + "integrity": "sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==", "dev": true, + "license": "MIT", "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", "agent-base": "^7.0.2", "debug": "^4.3.4", "get-uri": "^6.0.1", "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.2", - "pac-resolver": "^7.0.0", - "socks-proxy-agent": "^8.0.2" + "https-proxy-agent": "^7.0.5", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" }, "engines": { "node": ">= 14" @@ -14007,6 +14060,7 @@ "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", "dev": true, + "license": "MIT", "dependencies": { "degenerator": "^5.0.0", "netmask": "^2.0.2" @@ -14629,6 +14683,7 @@ "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", "dev": true, + "license": "MIT", "dependencies": { "agent-base": "^7.0.2", "debug": "^4.3.4", @@ -14648,6 +14703,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, + "license": "ISC", "engines": { "node": ">=12" } @@ -14685,35 +14741,40 @@ } }, "node_modules/puppeteer": { - "version": "22.8.1", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-22.8.1.tgz", - "integrity": "sha512-CFgPSKV+iydjO/8/hJVj251Hqp2PLcIa70j6H7sYqkwM8YJ+D3CA74Ufuj+yKtvDIntQPB/nLw4EHrHPcHOPjw==", + "version": "23.6.1", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-23.6.1.tgz", + "integrity": "sha512-8+ALGQgwXd3P/tGcuSsxTPGDaOQIjcDIm04I5hpWZv/PiN5q8bQNHRUyfYrifT+flnM9aTWCP7tLEzuB6SlIgA==", "dev": true, "hasInstallScript": true, + "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "2.2.3", - "cosmiconfig": "9.0.0", - "devtools-protocol": "0.0.1273771", - "puppeteer-core": "22.8.1" + "@puppeteer/browsers": "2.4.0", + "chromium-bidi": "0.8.0", + "cosmiconfig": "^9.0.0", + "devtools-protocol": "0.0.1354347", + "puppeteer-core": "23.6.1", + "typed-query-selector": "^2.12.0" }, "bin": { - "puppeteer": "lib/esm/puppeteer/node/cli.js" + "puppeteer": "lib/cjs/puppeteer/node/cli.js" }, "engines": { "node": ">=18" } }, "node_modules/puppeteer-core": { - "version": "22.8.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.8.1.tgz", - "integrity": "sha512-m1F6ZSTw1xrJ6xD4B+HonkSNVQmMrRMaqca/ivRcZYJ6jqzOnfEh3QgO9HpNPj6heiAZ2+4IPAU3jdZaTIDnSA==", + "version": "23.6.1", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.6.1.tgz", + "integrity": "sha512-DoNLAzQfGklPauEn33N4h9cM9GubJSINEn+AUMwAXwW159Y9JLk5y34Jsbv4c7kG8P0puOYWV9leu2siMZ/QpQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "2.2.3", - "chromium-bidi": "0.5.19", - "debug": "4.3.4", - "devtools-protocol": "0.0.1273771", - "ws": "8.17.0" + "@puppeteer/browsers": "2.4.0", + "chromium-bidi": "0.8.0", + "debug": "^4.3.7", + "devtools-protocol": "0.0.1354347", + "typed-query-selector": "^2.12.0", + "ws": "^8.18.0" }, "engines": { "node": ">=18" @@ -14766,7 +14827,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/randombytes": { "version": "2.1.0", @@ -15465,12 +15527,6 @@ "node": ">=4" } }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, "node_modules/send/node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -15708,6 +15764,7 @@ "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" @@ -15796,10 +15853,11 @@ } }, "node_modules/socks": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.1.tgz", - "integrity": "sha512-B6w7tkwNid7ToxjZ08rQMT8M9BJAf8DKx8Ft4NivzH0zBUfd6jldGcisJn/RLgxcX3FPNDdNQCUEMMT79b+oCQ==", + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", "dev": true, + "license": "MIT", "dependencies": { "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" @@ -15810,14 +15868,15 @@ } }, "node_modules/socks-proxy-agent": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.3.tgz", - "integrity": "sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A==", + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", + "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", "dev": true, + "license": "MIT", "dependencies": { "agent-base": "^7.1.1", "debug": "^4.3.4", - "socks": "^2.7.1" + "socks": "^2.8.3" }, "engines": { "node": ">= 14" @@ -16028,13 +16087,15 @@ } }, "node_modules/streamx": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.16.1.tgz", - "integrity": "sha512-m9QYj6WygWyWa3H1YY69amr4nVgy61xfjys7xO7kviL5rfIEc2naf+ewFiOA+aEJD7y0JO3h2GoiUv4TDwEGzQ==", + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.1.tgz", + "integrity": "sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA==", "dev": true, + "license": "MIT", "dependencies": { - "fast-fifo": "^1.1.0", - "queue-tick": "^1.0.1" + "fast-fifo": "^1.3.2", + "queue-tick": "^1.0.1", + "text-decoder": "^1.1.0" }, "optionalDependencies": { "bare-events": "^2.2.0" @@ -16256,10 +16317,11 @@ } }, "node_modules/tar-fs": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.5.tgz", - "integrity": "sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz", + "integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==", "dev": true, + "license": "MIT", "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" @@ -16274,6 +16336,7 @@ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "dev": true, + "license": "MIT", "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", @@ -16490,6 +16553,13 @@ "node": "*" } }, + "node_modules/text-decoder": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.1.tgz", + "integrity": "sha512-x9v3H/lTKIJKQQe7RPQkLfKAnc9lUTkWDypIQgTzPJAq+5/GCDHonmshfvlsNSj58yyshbIJJDLmU15qNERrXQ==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -16888,6 +16958,13 @@ "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==", "dev": true }, + "node_modules/typed-query-selector": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz", + "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==", + "dev": true, + "license": "MIT" + }, "node_modules/typescript": { "version": "5.4.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.4.tgz", @@ -17087,7 +17164,8 @@ "version": "10.0.0", "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/util-deprecate": { "version": "1.0.2", @@ -18133,10 +18211,11 @@ "dev": true }, "node_modules/ws": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", - "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -18299,10 +18378,11 @@ } }, "node_modules/zod": { - "version": "3.22.4", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", - "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/AdminWebsite/AdminWebsite/ClientApp/package.json b/AdminWebsite/AdminWebsite/ClientApp/package.json index a5c2a9a83..5d7205005 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/package.json +++ b/AdminWebsite/AdminWebsite/ClientApp/package.json @@ -87,7 +87,7 @@ "nswag": "^13.19.0", "pa11y": "^6.2.3", "prettier": "^2.8.8", - "puppeteer": "^22.8.1", + "puppeteer": "^23.6.0", "run-script-os": "^1.1.6", "sass": "^1.32.12", "typescript": "^5.4.4", diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/add-participant/add-participant.component.spec.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/add-participant/add-participant.component.spec.ts index a42169c44..f502778e2 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/add-participant/add-participant.component.spec.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/add-participant/add-participant.component.spec.ts @@ -261,6 +261,7 @@ videoHearingsServiceSpy = jasmine.createSpyObj([ 'getParticipantRoles', 'getCurrentRequest', 'setBookingHasChanged', + 'unsetBookingHasChanged', 'updateHearingRequest', 'cancelRequest', 'isConferenceClosed', @@ -975,18 +976,18 @@ describe('AddParticipantComponent', () => { }); describe('subcribeForSeachEmailEvents', () => { - it('should return errorAlternativeEmail & errorJohAccountNotFound as false if called with notFoundEmailEvent as false', () => { + it('should return errorAlternativeEmail & errorJohAccountNotFound as false if called with emailFoundEvent', () => { component.errorAlternativeEmail = true; component.errorJohAccountNotFound = true; component.subscribeForSearchEmailEvents(); - component.searchEmail.notFoundEmailEvent.next(false); + component.searchEmail.emailFoundEvent.next(); expect(component.errorAlternativeEmail).toBeFalsy(); expect(component.errorJohAccountNotFound).toBeFalsy(); }); it('should have called Not Found Participant if Not Found Email Event has been called', () => { spyOn(component, 'notFoundParticipant'); component.subscribeForSearchEmailEvents(); - component.searchEmail.notFoundEmailEvent.next(true); + component.searchEmail.emailNotFoundEvent.next(); expect(component.notFoundParticipant).toHaveBeenCalledTimes(1); }); }); @@ -1026,6 +1027,7 @@ describe('AddParticipantComponent edit mode', () => { 'getCurrentRequest', 'getParticipantRoles', 'setBookingHasChanged', + 'unsetBookingHasChanged', 'updateHearingRequest', 'cancelRequest', 'isConferenceClosed', @@ -1452,6 +1454,7 @@ describe('AddParticipantComponent edit mode no participants added', () => { 'getParticipantRoles', 'getCurrentRequest', 'setBookingHasChanged', + 'unsetBookingHasChanged', 'updateHearingRequest', 'cancelRequest', 'isConferenceClosed', diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/add-participant/add-participant.component.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/add-participant/add-participant.component.ts index 93bf40203..e29db5286 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/add-participant/add-participant.component.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/add-participant/add-participant.component.ts @@ -165,13 +165,12 @@ export class AddParticipantComponent extends AddParticipantBaseDirective impleme } subscribeForSearchEmailEvents() { - this.searchEmail.notFoundEmailEvent$.subscribe(notFound => { - if (notFound) { - this.notFoundParticipant(); - } else { - this.errorAlternativeEmail = false; - this.errorJohAccountNotFound = false; - } + this.searchEmail.emailFoundEvent$.subscribe(() => { + this.errorAlternativeEmail = false; + this.errorJohAccountNotFound = false; + }); + this.searchEmail.emailNotFoundEvent$.subscribe(() => { + this.notFoundParticipant(); }); } @@ -472,7 +471,7 @@ export class AddParticipantComponent extends AddParticipantBaseDirective impleme this.removeLinkedParticipant(this.selectedParticipantEmail); this.hearing = { ...this.hearing }; this.videoHearingService.updateHearingRequest(this.hearing); - this.videoHearingService.setBookingHasChanged(true); + this.videoHearingService.setBookingHasChanged(); } mapParticipant(newParticipant: ParticipantModel) { @@ -792,7 +791,7 @@ export class AddParticipantComponent extends AddParticipantBaseDirective impleme this.removeLinkedParticipant(this.selectedParticipantEmail); this.hearing = { ...this.hearing }; this.videoHearingService.updateHearingRequest(this.hearing); - this.videoHearingService.setBookingHasChanged(true); + this.videoHearingService.setBookingHasChanged(); } private addLinkedParticipant(newParticipant: ParticipantModel): void { diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/assign-judge/assign-judge.component.spec.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/assign-judge/assign-judge.component.spec.ts index 9ec145eb0..c5b45e7a0 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/assign-judge/assign-judge.component.spec.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/assign-judge/assign-judge.component.spec.ts @@ -93,6 +93,7 @@ describe('AssignJudgeComponent', () => { 'updateHearingRequest', 'cancelRequest', 'setBookingHasChanged', + 'unsetBookingHasChanged', 'canAddUser', 'canAddJudge' ]); diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/booking-base/booking-base.component.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/booking-base/booking-base.component.ts index 8a8df4d1e..e5188a216 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/booking-base/booking-base.component.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/booking-base/booking-base.component.ts @@ -33,7 +33,11 @@ export abstract class BookingBaseComponentDirective implements OnInit { this.buttonAction = this.editMode ? 'Save' : 'Next'; this.form.valueChanges.subscribe(() => { - this.videoHearingService.setBookingHasChanged(this.form.dirty); + if (this.form.dirty) { + this.videoHearingService.setBookingHasChanged(); + } else { + this.videoHearingService.unsetBookingHasChanged(); + } }); } diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/create-hearing/create-hearing.component.spec.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/create-hearing/create-hearing.component.spec.ts index 2a9304b40..8fcf54f45 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/create-hearing/create-hearing.component.spec.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/create-hearing/create-hearing.component.spec.ts @@ -69,7 +69,8 @@ describe('CreateHearingComponent with multiple case types', () => { 'getHearingTypes', 'getCurrentRequest', 'updateHearingRequest', - 'setBookingHasChanged' + 'setBookingHasChanged', + 'unsetBookingHasChanged' ]); routerSpy = jasmine.createSpyObj('Router', ['navigate']); @@ -212,7 +213,8 @@ describe('CreateHearingComponent with single case type', () => { 'getCurrentRequest', 'updateHearingRequest', 'cancelRequest', - 'setBookingHasChanged' + 'setBookingHasChanged', + 'unsetBookingHasChanged' ]); routerSpy = jasmine.createSpyObj('Router', ['navigate']); bookingServiceSpy = jasmine.createSpyObj('BookingSErvice', ['isEditMode', 'resetEditMode', 'removeEditMode']); @@ -278,7 +280,8 @@ describe('CreateHearingComponent with existing request in session', () => { 'getCurrentRequest', 'updateHearingRequest', 'cancelRequest', - 'setBookingHasChanged' + 'setBookingHasChanged', + 'unsetBookingHasChanged' ]); routerSpy = jasmine.createSpyObj('Router', ['navigate']); bookingServiceSpy = jasmine.createSpyObj('BookingSErvice', ['isEditMode', 'resetEditMode', 'removeEditMode']); diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/create-hearing/create-hearing.component.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/create-hearing/create-hearing.component.ts index 1882028c1..e86461003 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/create-hearing/create-hearing.component.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/create-hearing/create-hearing.component.ts @@ -75,7 +75,7 @@ export class CreateHearingComponent extends BookingBaseComponent implements OnIn if (this.isExistingHearingOrParticipantsAdded()) { this.form.get('supplier').disable(); } - } else if (this.form && this.form.contains('supplier')) { + } else if (this.form?.contains('supplier')) { this.form.removeControl('supplier'); this.hearing.supplier = this.retrieveDefaultSupplier(); } diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/endpoints/endpoints.component.spec.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/endpoints/endpoints.component.spec.ts index 698b83a9b..89a9c0e83 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/endpoints/endpoints.component.spec.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/endpoints/endpoints.component.spec.ts @@ -81,7 +81,8 @@ describe('EndpointsComponent', () => { 'isHearingAboutToStart', 'updateHearingRequest', 'cancelRequest', - 'setBookingHasChanged' + 'setBookingHasChanged', + 'unsetBookingHasChanged' ]); videoHearingsServiceSpy.getCurrentRequest.and.returnValue(newHearing); diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/endpoints/endpoints.component.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/endpoints/endpoints.component.ts index 149634ee1..dfd6a63e1 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/endpoints/endpoints.component.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/endpoints/endpoints.component.ts @@ -79,14 +79,12 @@ export class EndpointsComponent extends BookingBaseComponent implements OnInit, if (this.editMode || !canEditOtherInformation) { this.logger.debug(`${this.loggerPrefix} In edit mode. Returning to summary.`); this.router.navigate([PageUrls.Summary]); + } else if (this.specialMeasureEnabled) { + this.logger.debug(`${this.loggerPrefix} Proceeding to screening.`); + this.router.navigate([PageUrls.Screening]); } else { - if (this.specialMeasureEnabled) { - this.logger.debug(`${this.loggerPrefix} Proceeding to screening.`); - this.router.navigate([PageUrls.Screening]); - } else { - this.logger.debug(`${this.loggerPrefix} Proceeding to other information.`); - this.router.navigate([PageUrls.OtherInformation]); - } + this.logger.debug(`${this.loggerPrefix} Proceeding to other information.`); + this.router.navigate([PageUrls.OtherInformation]); } } @@ -153,7 +151,7 @@ export class EndpointsComponent extends BookingBaseComponent implements OnInit, // If no such endpoint exists, push the new endpoint to the videoEndpoints array if (!existingEndpoint) { this.videoEndpoints.push($event); - this.videoHearingService.setBookingHasChanged(true); + this.videoHearingService.setBookingHasChanged(); } this.upsertEndpointsToBooking(); } @@ -166,14 +164,14 @@ export class EndpointsComponent extends BookingBaseComponent implements OnInit, if (index !== -1) { this.videoEndpoints[index] = $event.updated; this.videoEndpointToEdit = null; - this.videoHearingService.setBookingHasChanged(true); + this.videoHearingService.setBookingHasChanged(); } this.upsertEndpointsToBooking(); } onEndpointSelectedForDeletion(existingEndpoint: VideoAccessPointDto) { this.videoEndpoints = this.videoEndpoints.filter(endpoint => endpoint.displayName !== existingEndpoint.displayName); - this.videoHearingService.setBookingHasChanged(true); + this.videoHearingService.setBookingHasChanged(); this.upsertEndpointsToBooking(); } diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/hearing-schedule/hearing-schedule.component.spec.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/hearing-schedule/hearing-schedule.component.spec.ts index e99ac820d..92d2c7821 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/hearing-schedule/hearing-schedule.component.spec.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/hearing-schedule/hearing-schedule.component.spec.ts @@ -87,7 +87,8 @@ describe('HearingScheduleComponent first visit', () => { 'getCurrentRequest', 'updateHearingRequest', 'cancelRequest', - 'setBookingHasChanged' + 'setBookingHasChanged', + 'unsetBookingHasChanged' ]); videoHearingsServiceSpy.getCurrentRequest.and.returnValue(newHearing); @@ -416,7 +417,8 @@ describe('HearingScheduleComponent returning to page', () => { 'getCurrentRequest', 'updateHearingRequest', 'cancelRequest', - 'setBookingHasChanged' + 'setBookingHasChanged', + 'unsetBookingHasChanged' ]); videoHearingsServiceSpy.getCurrentRequest.and.returnValue(existingRequest); @@ -580,7 +582,8 @@ describe('HearingScheduleComponent multi days hearing', () => { 'getCurrentRequest', 'updateHearingRequest', 'cancelRequest', - 'setBookingHasChanged' + 'setBookingHasChanged', + 'unsetBookingHasChanged' ]); videoHearingsServiceSpy.getCurrentRequest.and.returnValue(existingRequest); diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/hearing-schedule/hearing-schedule.component.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/hearing-schedule/hearing-schedule.component.ts index 796dc6bc9..3141ea5a5 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/hearing-schedule/hearing-schedule.component.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/hearing-schedule/hearing-schedule.component.ts @@ -49,6 +49,15 @@ export class HearingScheduleComponent extends BookingBaseComponent implements On hearingsInGroupToEdit: HearingModel[]; newDatesFormArray: FormArray; + hearingDateParsed: string = null; + startTimeHour: string = null; + startTimeMinute: string = null; + durationHour: string = null; + durationMinute: string = null; + room = ''; + endHearingDateParsed: string = null; + multiDaysRange = true; + private readonly destroyed$ = new Subject(); multiDayBookingEnhancementsEnabled: boolean; @@ -94,97 +103,111 @@ export class HearingScheduleComponent extends BookingBaseComponent implements On } private initForm() { - let hearingDateParsed = null; - let startTimeHour = null; - let startTimeMinute = null; - let durationHour = null; - let durationMinute = null; - let room = ''; this.multiDaysHearing = null; - let endHearingDateParsed = null; - let multiDaysRange = true; + this.initialiseHearingDetails(); + this.setUpDurationControls(); + this.buildFormGroup(); + if (this.hearing?.isMultiDayEdit) { + this.setUpNewDateControls(); + } + this.subscribeToFormChanges(); + } - if (this.hearing) { - this.logger.debug(`${this.loggerPrefix} Populating form with existing hearing details`, { - hearing: this.hearing?.hearing_id - }); - if (this.hearing.hearing_venue_id === undefined) { - this.hearing.hearing_venue_id = -1; - } + private initialiseHearingDetails() { + if (!this.hearing) { + return; + } + this.logger.debug(`${this.loggerPrefix} Populating form with existing hearing details`, { + hearing: this.hearing?.hearing_id + }); + if (this.hearing.hearing_venue_id === undefined) { + this.hearing.hearing_venue_id = -1; + } - if (this.hearing.scheduled_date_time) { - const date = new Date(this.hearing.scheduled_date_time); - hearingDateParsed = this.datePipe.transform(date, 'yyyy-MM-dd'); - startTimeHour = (date.getHours() < 10 ? '0' : '') + date.getHours(); - startTimeMinute = (date.getMinutes() < 10 ? '0' : '') + date.getMinutes(); - } + this.setStartTime(); - if (this.hearing.end_hearing_date_time) { - const date = new Date(this.hearing.end_hearing_date_time); - endHearingDateParsed = this.datePipe.transform(date, 'yyyy-MM-dd'); - } + if (this.hearing.end_hearing_date_time) { + const date = new Date(this.hearing.end_hearing_date_time); + this.endHearingDateParsed = this.datePipe.transform(date, 'yyyy-MM-dd'); + } - if (this.hearing.hearing_dates.length > 0) { - this.hearingDates = this.hearing.hearing_dates.map(x => new Date(x)); - multiDaysRange = false; - } + if (this.hearing.hearing_dates.length > 0) { + this.hearingDates = this.hearing.hearing_dates.map(x => new Date(x)); + this.multiDaysRange = false; + } - if (this.hearing.scheduled_duration) { - const duration = new Date(); - duration.setHours(0, 0, 0, 0); - duration.setMinutes(this.hearing.scheduled_duration); - durationHour = (duration.getHours() < 10 ? '0' : '') + duration.getHours(); - durationMinute = (duration.getMinutes() < 10 ? '0' : '') + duration.getMinutes(); - } + this.setDuration(); - if (this.hearing.scheduled_date_time && this.hearing.scheduled_duration && this.hearing.hearing_venue_id) { - this.hasSaved = true; - } + if (this.hearing.scheduled_date_time && this.hearing.scheduled_duration && this.hearing.hearing_venue_id) { + this.hasSaved = true; + } - if (this.hearing.court_room) { - room = this.hearing.court_room; - } + if (this.hearing.court_room) { + this.room = this.hearing.court_room; + } - this.multiDaysHearing = this.hearing.isMultiDayEdit; + this.multiDaysHearing = this.hearing.isMultiDayEdit; - if (this.hearing.isMultiDayEdit && this.hearing.multiDayHearingLastDayScheduledDateTime) { - const date = new Date(this.hearing.multiDayHearingLastDayScheduledDateTime); - endHearingDateParsed = this.datePipe.transform(date, 'yyyy-MM-dd'); - } + if (this.hearing.isMultiDayEdit && this.hearing.multiDayHearingLastDayScheduledDateTime) { + const date = new Date(this.hearing.multiDayHearingLastDayScheduledDateTime); + this.endHearingDateParsed = this.datePipe.transform(date, 'yyyy-MM-dd'); } + this.selectedCourtName = this.hearing.court_name; + this.selectedCourtCode = this.hearing.court_code; + } + + private setStartTime() { + if (!this.hearing.scheduled_date_time) { + return; + } + const date = new Date(this.hearing.scheduled_date_time); + this.hearingDateParsed = this.datePipe.transform(date, 'yyyy-MM-dd'); + this.startTimeHour = (date.getHours() < 10 ? '0' : '') + date.getHours(); + this.startTimeMinute = (date.getMinutes() < 10 ? '0' : '') + date.getMinutes(); + } + + private setDuration() { + if (!this.hearing.scheduled_duration) { + return; + } + const duration = new Date(); + duration.setHours(0, 0, 0, 0); + duration.setMinutes(this.hearing.scheduled_duration); + this.durationHour = (duration.getHours() < 10 ? '0' : '') + duration.getHours(); + this.durationMinute = (duration.getMinutes() < 10 ? '0' : '') + duration.getMinutes(); + } + + private setUpDurationControls() { if (!this.showDurationControls) { - this.durationHourControl = new FormControl(durationHour); - this.durationMinuteControl = new FormControl(durationMinute); + this.durationHourControl = new FormControl(this.durationHour); + this.durationMinuteControl = new FormControl(this.durationMinute); } else { - this.durationHourControl = new FormControl(durationHour, [Validators.required, Validators.min(0), Validators.max(23)]); - this.durationMinuteControl = new FormControl(durationMinute, [Validators.required, Validators.min(0), Validators.max(59)]); + this.durationHourControl = new FormControl(this.durationHour, [Validators.required, Validators.min(0), Validators.max(23)]); + this.durationMinuteControl = new FormControl(this.durationMinute, [Validators.required, Validators.min(0), Validators.max(59)]); } + } - this.selectedCourtName = this.hearing.court_name; - this.selectedCourtCode = this.hearing.court_code; - + private buildFormGroup() { this.newDatesFormArray = this.formBuilder.array([]); this.form = this.formBuilder.group({ - hearingDate: [hearingDateParsed, [Validators.required, pastDateValidator()]], - hearingStartTimeHour: [startTimeHour, [Validators.required, Validators.min(0), Validators.max(23)]], - hearingStartTimeMinute: [startTimeMinute, [Validators.required, Validators.min(0), Validators.max(59)]], + hearingDate: [this.hearingDateParsed, [Validators.required, pastDateValidator()]], + hearingStartTimeHour: [this.startTimeHour, [Validators.required, Validators.min(0), Validators.max(23)]], + hearingStartTimeMinute: [this.startTimeMinute, [Validators.required, Validators.min(0), Validators.max(59)]], hearingDurationHour: this.durationHourControl, hearingDurationMinute: this.durationMinuteControl, courtAddress: [this.hearing.hearing_venue_id, [Validators.required, Validators.min(1)]], - courtRoom: [room, [Validators.pattern(Constants.TextInputPatternDisplayName), Validators.maxLength(255)]], + courtRoom: [this.room, [Validators.pattern(Constants.TextInputPatternDisplayName), Validators.maxLength(255)]], multiDays: [this.multiDaysHearing], - endHearingDate: [endHearingDateParsed], - multiDaysRange: [multiDaysRange], + endHearingDate: [this.endHearingDateParsed], + multiDaysRange: [this.multiDaysRange], newDates: this.newDatesFormArray }); + } - if (this.hearing?.isMultiDayEdit) { - this.setUpNewDateControls(); - } - + private subscribeToFormChanges() { ['multiDays', 'multiDaysRange'].forEach(k => { this.form.get(k).valueChanges.subscribe(() => { this.multiDaysChanged(); diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/other-information/other-information.component.spec.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/other-information/other-information.component.spec.ts index dd8787ae6..65a12f6e9 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/other-information/other-information.component.spec.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/other-information/other-information.component.spec.ts @@ -65,7 +65,8 @@ describe('OtherInformationComponent', () => { 'getCurrentRequest', 'cancelRequest', 'updateHearingRequest', - 'setBookingHasChanged' + 'setBookingHasChanged', + 'unsetBookingHasChanged' ]); beforeEach(waitForAsync(() => { diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/other-information/other-information.component.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/other-information/other-information.component.ts index 6ca76c0f8..061b38506 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/other-information/other-information.component.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/other-information/other-information.component.ts @@ -85,7 +85,7 @@ export class OtherInformationComponent extends BookingBaseComponent implements O return true; } - return this.hearing && this.hearing.audio_recording_required !== null && this.hearing.audio_recording_required !== undefined + return this.hearing?.audio_recording_required !== null && this.hearing?.audio_recording_required !== undefined ? this.hearing.audio_recording_required : true; } diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/search-email/search-email.component.spec.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/search-email/search-email.component.spec.ts index 2b53d06f7..7145d65d4 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/search-email/search-email.component.spec.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/search-email/search-email.component.spec.ts @@ -344,11 +344,11 @@ describe('SearchEmailComponent', () => { expect(component.notFoundParticipant).toBeFalsy(); }); it('should set notFoundParticipant to true', () => { - spyOn(component.notFoundEmailEvent, 'next'); + spyOn(component.emailNotFoundEvent, 'next'); component.noDataFound(); expect(component.isShowResult).toBeFalsy(); expect(component.notFoundParticipant).toBeTruthy(); - expect(component.notFoundEmailEvent.next).toHaveBeenCalled(); + expect(component.emailNotFoundEvent.next).toHaveBeenCalled(); }); it('should set notFoundParticipant to false if less that 3 letters input', () => { component.lessThanThreeLetters(); @@ -370,10 +370,9 @@ describe('SearchEmailComponent', () => { it('should set correct errors when too few characters', fakeAsync(() => { component.isShowResult = true; component.notFoundParticipant = true; - component.notFoundEmailEvent.next(true); + component.emailNotFoundEvent.next(); - const subscription = component.notFoundEmailEvent.subscribe(emailEvent => { - expect(emailEvent).toBe(false); + const subscription = component.emailFoundEvent.subscribe(() => { expect(component.isShowResult).toBe(false); expect(component.notFoundParticipant).toBe(false); diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/search-email/search-email.component.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/search-email/search-email.component.ts index 301566cef..74324a729 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/search-email/search-email.component.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/search-email/search-email.component.ts @@ -29,8 +29,10 @@ export class SearchEmailComponent implements OnInit, OnDestroy { invalidPattern: string; isErrorEmailAssignedToJudge = false; isJoh = false; - notFoundEmailEvent = new Subject(); - notFoundEmailEvent$ = this.notFoundEmailEvent.asObservable(); + emailFoundEvent = new Subject(); + emailFoundEvent$ = this.emailFoundEvent.asObservable(); + emailNotFoundEvent = new Subject(); + emailNotFoundEvent$ = this.emailNotFoundEvent.asObservable(); private readonly cannotAddNewUsersRoles = [this.constants.Judge]; @Input() disabled = true; @@ -107,19 +109,19 @@ export class SearchEmailComponent implements OnInit, OnDestroy { this.isShowResult = true; this.isValidEmail = true; this.notFoundParticipant = false; - this.notFoundEmailEvent.next(false); + this.emailFoundEvent.next(); } noDataFound() { this.isShowResult = false; this.notFoundParticipant = !this.isErrorEmailAssignedToJudge; - this.notFoundEmailEvent.next(true); + this.emailNotFoundEvent.next(); } lessThanThreeLetters() { this.isShowResult = false; this.notFoundParticipant = false; - this.notFoundEmailEvent.next(false); + this.emailFoundEvent.next(); } selectItemClick(result: ParticipantModel) { diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/summary/summary.component.spec.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/summary/summary.component.spec.ts index 369e6ed88..08fb80aa1 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/summary/summary.component.spec.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/summary/summary.component.spec.ts @@ -115,6 +115,7 @@ const videoHearingsServiceSpy: jasmine.SpyObj = jasmine.cr 'cancelRequest', 'updateHearing', 'setBookingHasChanged', + 'unsetBookingHasChanged', 'cloneMultiHearings', 'isConferenceClosed', 'isHearingAboutToStart', diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/summary/summary.component.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/summary/summary.component.ts index 280121e3f..b6ec257ca 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/summary/summary.component.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/booking/summary/summary.component.ts @@ -215,7 +215,7 @@ export class SummaryComponent implements OnInit, OnDestroy { } this.hearingService.updateHearingRequest(this.hearing); - this.hearingService.setBookingHasChanged(true); + this.hearingService.setBookingHasChanged(); this.bookingService.removeParticipantEmail(); } @@ -424,7 +424,7 @@ export class SummaryComponent implements OnInit, OnDestroy { const noJudgePrior = this.hearing.status === BookingStatus.BookedWithoutJudge || this.hearing.status === BookingStatus.ConfirmedWithoutJudge; this.showWaitSaving = false; - this.hearingService.setBookingHasChanged(false); + this.hearingService.unsetBookingHasChanged(); this.logger.info(`${this.loggerPrefix} Updated booking. Navigating to booking details.`, { hearingId: hearingDetailsResponse.id }); @@ -539,7 +539,7 @@ export class SummaryComponent implements OnInit, OnDestroy { this.removeLinkedParticipant(this.selectedParticipantEmail); this.hearing = { ...this.hearing }; this.hearingService.updateHearingRequest(this.hearing); - this.hearingService.setBookingHasChanged(true); + this.hearingService.setBookingHasChanged(); this.bookingService.removeParticipantEmail(); } diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/change-password/change-password.component.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/change-password/change-password.component.ts index f1aa8c04e..3e0c277f1 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/change-password/change-password.component.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/change-password/change-password.component.ts @@ -62,20 +62,20 @@ export class ChangePasswordComponent implements OnInit, OnDestroy { this.saveSuccess = false; this.logger.debug(`${this.loggerPrefix} Attempting to reset password for user.`, { username: this.userName.value }); - this.$subcription = this.userDataService.updateUser(this.userName.value).subscribe( - () => { + this.$subcription = this.userDataService.updateUser(this.userName.value).subscribe({ + next: () => { // tslint:disable-next-line: quotemark this.popupMessage = "User's password has been changed"; this.showUpdateSuccess = true; this.logger.info(`${this.loggerPrefix} User password has been reset.`, { username: this.userName.value }); this.saveSuccess = true; }, - error => { + error: error => { this.popupMessage = 'User does not exist - please try again'; this.showUpdateSuccess = true; this.logger.error(`${this.loggerPrefix} User does not exist.`, error, { username: this.userName.value }); } - ); + }); } else { this.failedSubmission = true; } diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/dashboard/unallocated-hearings/unallocated-hearings.component.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/dashboard/unallocated-hearings/unallocated-hearings.component.ts index a9f97713b..06a0ef0e4 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/dashboard/unallocated-hearings/unallocated-hearings.component.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/dashboard/unallocated-hearings/unallocated-hearings.component.ts @@ -40,13 +40,15 @@ export class UnallocatedHearingsComponent implements OnInit { } ngOnInit(): void { - this.client.getUnallocatedHearings().subscribe( - result => { + this.client.getUnallocatedHearings().subscribe({ + next: result => { this.unallocatedHearings = result; this.setRouterParameters(); }, - error => this.logger.error(`${this.loggerPrefix} Could not get unallocated hearings`, error) - ); + error: error => { + this.logger.error(`${this.loggerPrefix} Could not get unallocated hearings`, error); + } + }); } private setRouterParameters() { const format = (dt: Date) => dt.toISOString().split('T')[0]; diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/security/reform-login.component.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/security/reform-login.component.ts index e46f0dad6..17278c065 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/security/reform-login.component.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/security/reform-login.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { IdpProviders, SecurityService } from './services/security.service'; import { PageUrls } from '../shared/page-url.constants'; @@ -6,9 +6,12 @@ import { PageUrls } from '../shared/page-url.constants'; selector: 'app-reform-login', templateUrl: './login.component.html' }) -export class ReformLoginComponent { +export class ReformLoginComponent implements OnInit { constructor(private readonly router: Router, private readonly securityService: SecurityService) { this.securityService.currentIdpConfigId = IdpProviders.reform; + } + + ngOnInit(): void { this.router.navigate([`/${PageUrls.Login}`]); } } diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/services/bookings-list.service.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/services/bookings-list.service.ts index 18c342500..5d90f3df4 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/services/bookings-list.service.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/services/bookings-list.service.ts @@ -33,6 +33,12 @@ export class BookingsListService { noJudge?: boolean, noAllocated?: boolean ): Observable { + if (noJudge == null || undefined) { + noJudge = false; + } + if (noAllocated == null || undefined) { + noAllocated = false; + } const searchRequest = { cursor, limit, diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/services/justice-users.service.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/services/justice-users.service.ts index 7ee59aa34..99fd6dee5 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/services/justice-users.service.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/services/justice-users.service.ts @@ -95,7 +95,7 @@ export class JusticeUsersService { return this.apiClient.getUserList(cleanQuery(term)).pipe( catchError(error => { this.logger.error(`${this.loggerPrefix} There was an unexpected error getting justice users`, new Error(error)); - return throwError(error); + return throwError(() => error); }) ); } diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/services/participant-edit-service.service.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/services/participant-edit-service.service.ts index 48829f3c0..7c887ca01 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/services/participant-edit-service.service.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/services/participant-edit-service.service.ts @@ -26,7 +26,7 @@ export class ParticipantEditService { } catch (error) { this.logger.error(`Failed to find person ${contactEmail}. ${error.response}`, error); if (BookHearingException.isBookHearingException(error)) { - throw error as BookHearingException; + throw error; } return null; } diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/services/video-hearings.service.spec.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/services/video-hearings.service.spec.ts index d62ef6f37..4aad66272 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/services/video-hearings.service.spec.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/services/video-hearings.service.spec.ts @@ -76,9 +76,9 @@ describe('Video hearing service', () => { }); it('should not have changes if we set it to false', () => { - service.setBookingHasChanged(true); + service.setBookingHasChanged(); expect(service.hasUnsavedChanges()).toBe(true); - service.setBookingHasChanged(false); + service.unsetBookingHasChanged(); expect(service.hasUnsavedChanges()).toBe(false); }); @@ -689,9 +689,9 @@ describe('Video hearing service', () => { }); it('should not have changes if we set it to false', () => { - service.setVhoNonAvailabiltiesHaveChanged(true); + service.setVhoNonAvailabiltiesHaveChanged(); expect(service.hasUnsavedVhoNonAvailabilityChanges()).toBe(true); - service.setVhoNonAvailabiltiesHaveChanged(false); + service.unsetVhoNonAvailabiltiesHaveChanged(); expect(service.hasUnsavedVhoNonAvailabilityChanges()).toBe(false); }); }); diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/services/video-hearings.service.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/services/video-hearings.service.ts index 596c490e7..2b99361e9 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/services/video-hearings.service.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/services/video-hearings.service.ts @@ -96,20 +96,20 @@ export class VideoHearingsService { return !!request; } - setBookingHasChanged(isChanged: boolean) { - if (isChanged) { - sessionStorage.setItem(this.bookingHasChangesKey, 'true'); - } else { - sessionStorage.removeItem(this.bookingHasChangesKey); - } + setBookingHasChanged() { + sessionStorage.setItem(this.bookingHasChangesKey, 'true'); } - setVhoNonAvailabiltiesHaveChanged(isChanged: boolean) { - if (isChanged) { - sessionStorage.setItem(this.vhoNonAvailabiltiesHaveChangesKey, 'true'); - } else { - sessionStorage.removeItem(this.vhoNonAvailabiltiesHaveChangesKey); - } + unsetBookingHasChanged() { + sessionStorage.removeItem(this.bookingHasChangesKey); + } + + setVhoNonAvailabiltiesHaveChanged() { + sessionStorage.setItem(this.vhoNonAvailabiltiesHaveChangesKey, 'true'); + } + + unsetVhoNonAvailabiltiesHaveChanged() { + sessionStorage.removeItem(this.vhoNonAvailabiltiesHaveChangesKey); } cancelVhoNonAvailabiltiesRequest() { diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/shared/menus/case-types-menu/case-types-menu.component.spec.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/shared/menus/case-types-menu/case-types-menu.component.spec.ts index f875a3f67..333bda07b 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/shared/menus/case-types-menu/case-types-menu.component.spec.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/shared/menus/case-types-menu/case-types-menu.component.spec.ts @@ -50,11 +50,11 @@ describe('CaseTypesMenuComponent', () => { describe('enable', () => { it('should call base enable function, to enable this component', () => { - component.enabled(true); + component.enabled(); expect(component.form.controls[component.formGroupName].enabled).toEqual(true); }); it('should call base enable function, to disable this component', () => { - component.enabled(false); + component.disabled(); expect(component.form.controls[component.formGroupName].enabled).toEqual(false); }); }); diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/shared/menus/justice-users-menu/justice-users-menu.component.spec.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/shared/menus/justice-users-menu/justice-users-menu.component.spec.ts index 81b51fcbc..6d3a88ef6 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/shared/menus/justice-users-menu/justice-users-menu.component.spec.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/shared/menus/justice-users-menu/justice-users-menu.component.spec.ts @@ -53,11 +53,11 @@ describe('JusticeUsersMenuComponent', () => { describe('enable', () => { it('should call base enable function, to enable this component', () => { - component.enabled(true); + component.enabled(); expect(component.form.controls[component.formGroupName].enabled).toEqual(true); }); it('should call base enable function, to disable this component', () => { - component.enabled(false); + component.disabled(); expect(component.form.controls[component.formGroupName].enabled).toEqual(false); }); }); diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/shared/menus/menu-base.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/shared/menus/menu-base.ts index 73842842b..e64782f38 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/shared/menus/menu-base.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/shared/menus/menu-base.ts @@ -22,12 +22,11 @@ export abstract class MenuBase implements OnInit { abstract formConfiguration: any; @Output() selectedEmitter = new EventEmitter(); - enabled(value: boolean) { - if (value !== false) { - this.form.controls[this.formGroupName].enable(); - } else { - this.form.controls[this.formGroupName].disable(); - } + enabled() { + this.form.controls[this.formGroupName].enable(); + } + disabled() { + this.form.controls[this.formGroupName].disable(); } abstract loadItems(): void; diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/shared/menus/venues-menu/venues-menu.component.spec.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/shared/menus/venues-menu/venues-menu.component.spec.ts index ad66e9d01..c07a160b9 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/shared/menus/venues-menu/venues-menu.component.spec.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/shared/menus/venues-menu/venues-menu.component.spec.ts @@ -48,11 +48,11 @@ describe('VenuesMenuComponent', () => { describe('enable', () => { it('should call base enable function, to enable this component', () => { - component.enabled(true); + component.enabled(); expect(component.form.controls[component.formGroupName].enabled).toEqual(true); }); it('should call base enable function, to disable this component', () => { - component.enabled(false); + component.disabled(); expect(component.form.controls[component.formGroupName].enabled).toEqual(false); }); }); diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/shared/window-scrolling.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/shared/window-scrolling.ts index bd89e54da..c214a9ce6 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/shared/window-scrolling.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/shared/window-scrolling.ts @@ -6,7 +6,7 @@ export class WindowScrolling { constructor(@Inject(DOCUMENT) private readonly document: Document) {} getPosition(): number { - return window.pageYOffset; + return window.scrollY; } getWindowHeight(): number { diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/src/app/shared/feature-flag.directive.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/src/app/shared/feature-flag.directive.ts index c2a02c052..ebab27af3 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/src/app/shared/feature-flag.directive.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/src/app/shared/feature-flag.directive.ts @@ -18,12 +18,16 @@ export class FeatureFlagDirective { @Input() set appFeatureFlag(flagKey: keyof typeof FeatureFlags) { this.subscription?.unsubscribe(); - this.subscription = this.launchDarklyService.getFlag(flagKey).subscribe(flagValue => { - if (flagValue) { - this.viewContainer.createEmbeddedView(this.templateRef); - } else { - this.viewContainer.clear(); - } + this.subscription = this.launchDarklyService.getFlag(flagKey).subscribe({ + next: flagValue => (flagValue ? this.showContent() : this.hideContent()) }); } + + private showContent(): void { + this.viewContainer.createEmbeddedView(this.templateRef); + } + + private hideContent(): void { + this.viewContainer.clear(); + } } diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/allocate-hearings/allocate-hearings.component.html b/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/allocate-hearings/allocate-hearings.component.html index e8c51fc0c..298b53460 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/allocate-hearings/allocate-hearings.component.html +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/allocate-hearings/allocate-hearings.component.html @@ -144,7 +144,7 @@ type="checkbox" [checked]="allocationHearingViewModel.areAllChecked" aria-label="Select all hearings" - (change)="toggleAll(!allocationHearingViewModel.areAllChecked)" + (change)="toggleAll()" /> Hearing date @@ -167,8 +167,8 @@ type="checkbox" [checked]="hearing.checked" aria-label="Select hearing" - (click)="selectHearing(!hearing.checked, hearing.hearingId)" - (keydown)="selectHearing(!hearing.checked, hearing.hearingId)" + (click)="toggleSelectHearing(hearing)" + (keydown)="toggleSelectHearing(hearing)" /> {{ hearing.scheduledDateTime.toLocaleDateString('en-GB') }} diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/allocate-hearings/allocate-hearings.component.spec.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/allocate-hearings/allocate-hearings.component.spec.ts index e7c71abd1..92624ff1a 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/allocate-hearings/allocate-hearings.component.spec.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/allocate-hearings/allocate-hearings.component.spec.ts @@ -336,7 +336,7 @@ describe('AllocateHearingsComponent', () => { const username = 'test@cso.com'; // act - component.selectHearing(true, hearingId); + component.selectHearing(hearingId); // mimic cso selection component.onJusticeUserForAllocationSelected({ entityId: csoId, data: username, label: '' }); @@ -365,7 +365,7 @@ describe('AllocateHearingsComponent', () => { // act // first allocate all hearings - component.toggleAll(true); + component.selectAll(); fixture.detectChanges(); const postUpdateHearing = component.allocationHearingViewModel.hearings.find(x => x.hearingId === hearingId); @@ -375,14 +375,14 @@ describe('AllocateHearingsComponent', () => { expect(matchingElements.every(x => (x.nativeElement).innerText === username)).toBeTruthy(); // then uncheck one - component.selectHearing(false, hearingId); + component.deselectHearing(hearingId); fixture.detectChanges(); const postRevertedHearing = component.allocationHearingViewModel.hearings.find(x => x.hearingId === hearingId); expect(postRevertedHearing.allocatedOfficerUsername).toBe(originalUsername); // then toggle all off - component.toggleAll(false); + component.deselectAll(); fixture.detectChanges(); expect(component.allocationHearingViewModel.originalState).toEqual(testData); const revertedMatchingElements = fixture.debugElement.queryAll(By.css('[id^=cso_]')); @@ -411,7 +411,7 @@ describe('AllocateHearingsComponent', () => { allocateServiceSpy.allocateCsoToHearings.and.returnValue(of([updatedAllocation])); // act - component.selectHearing(true, hearingId); + component.selectHearing(hearingId); // mimic cso selection component.onJusticeUserForAllocationSelected({ entityId: csoId, label: '' }); @@ -449,7 +449,7 @@ describe('AllocateHearingsComponent', () => { const spy = spyOn(component, 'clearHearingUpdatedMessage'); // act - component.selectHearing(true, hearingId); + component.selectHearing(hearingId); component.onJusticeUserForAllocationSelected({ entityId: csoId, label: '' }); component.confirmAllocation(); tick(); @@ -487,7 +487,7 @@ describe('AllocateHearingsComponent', () => { const csoId = newGuid(); // act - component.selectHearing(true, hearingId); + component.selectHearing(hearingId); component.cancelAllocation(); @@ -502,7 +502,7 @@ describe('AllocateHearingsComponent', () => { const hearingId = testData[0].hearing_id; // act - component.selectHearing(true, hearingId); + component.selectHearing(hearingId); // mimic cso selection clear component.onJusticeUserForAllocationSelected(); diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/allocate-hearings/allocate-hearings.component.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/allocate-hearings/allocate-hearings.component.ts index 495a25103..ee9228d88 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/allocate-hearings/allocate-hearings.component.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/allocate-hearings/allocate-hearings.component.ts @@ -2,7 +2,7 @@ import { Component, OnInit, ViewChild } from '@angular/core'; import { AllocateHearingsService } from '../services/allocate-hearings.service'; import { AllocationHearingsResponse, JusticeUserResponse } from '../../services/clients/api-client'; import { faCircleExclamation, faHourglassStart, faTriangleExclamation, faClock } from '@fortawesome/free-solid-svg-icons'; -import { AllocateHearingModel } from './models/allocate-hearing.model'; +import { AllocateHearingItemModel, AllocateHearingModel } from './models/allocate-hearing.model'; import { Transform } from '@fortawesome/fontawesome-svg-core'; import { Constants } from 'src/app/common/constants'; import { DatePipe } from '@angular/common'; @@ -84,7 +84,11 @@ export class AllocateHearingsComponent implements OnInit { }); this.form.get('isUnallocated').valueChanges.subscribe(val => { - this.onIsAllocatedCheckboxChanged(val); + if (val) { + this.onIsAllocatedCheckboxChecked(); + } else { + this.onIsAllocatedCheckboxUnchecked(); + } }); this.selectedJusticeUserIds = this.bookingPersistService.selectedUsers; @@ -176,13 +180,13 @@ export class AllocateHearingsComponent implements OnInit { } } - onIsAllocatedCheckboxChanged(checked: boolean) { - if (checked) { - this.selectFilterCso.clear(); - this.selectFilterCso.disable(); - } else { - this.selectFilterCso.enable(); - } + onIsAllocatedCheckboxChecked() { + this.selectFilterCso.clear(); + this.selectFilterCso.disable(); + } + + onIsAllocatedCheckboxUnchecked() { + this.selectFilterCso.enable(); } onJusticeUserForAllocationSelected(selectedItem?: SelectOption) { @@ -193,7 +197,7 @@ export class AllocateHearingsComponent implements OnInit { } else { // without a selected CSO, unset selection this.clearMessage(); - this.toggleAll(false); + this.deselectAll(); } } @@ -223,7 +227,7 @@ export class AllocateHearingsComponent implements OnInit { } cancelAllocation() { - this.toggleAll(false); + this.deselectAll(); this.selectAllocateCso.clear(); this.clearMessage(); this.allocationHearingViewModel.uncheckAllHearingsAndRevert(); @@ -241,20 +245,28 @@ export class AllocateHearingsComponent implements OnInit { }); } - toggleAll(checkAll: boolean) { - if (checkAll) { - this.allocationHearingViewModel.checkAllHearings(); - const selectedCso = this.selectAllocateCso?.selected as SelectOption; - if (selectedCso) { - const csoId = selectedCso.entityId; - const csoUsername = selectedCso.data; - this.attemptToAssignCsoToSelectedHearings(csoId, csoUsername); - } + toggleAll() { + if (this.allocationHearingViewModel.areAllChecked) { + this.deselectAll(); } else { - this.allocationHearingViewModel.uncheckAllHearingsAndRevert(); + this.selectAll(); + } + } + + selectAll() { + this.allocationHearingViewModel.checkAllHearings(); + const selectedCso = this.selectAllocateCso?.selected as SelectOption; + if (selectedCso) { + const csoId = selectedCso.entityId; + const csoUsername = selectedCso.data; + this.attemptToAssignCsoToSelectedHearings(csoId, csoUsername); } } + deselectAll() { + this.allocationHearingViewModel.uncheckAllHearingsAndRevert(); + } + private updateTableWithAllocatedCso(updatedHearings: AllocationHearingsResponse[]) { this.allocationHearingViewModel.updateHearings(updatedHearings); this.allocationHearingViewModel.uncheckAllHearingsAndRevert(); @@ -262,23 +274,31 @@ export class AllocateHearingsComponent implements OnInit { this.updateMessageAndDisplay(Constants.AllocateHearings.ConfirmationMessage); } - selectHearing(checked: boolean, hearing_id: string) { - if (checked) { - this.clearHearingUpdatedMessage(); - this.allocationHearingViewModel.checkHearing(hearing_id); + toggleSelectHearing(hearing: AllocateHearingItemModel) { + if (hearing.checked) { + this.deselectHearing(hearing.hearingId); + } else { + this.selectHearing(hearing.hearingId); + } + } + + selectHearing(hearing_id: string) { + this.clearHearingUpdatedMessage(); + this.allocationHearingViewModel.checkHearing(hearing_id); - const selectedCso = this.selectAllocateCso?.selected as SelectOption; - if (selectedCso) { - const csoId = selectedCso.entityId; - const csoUsername = selectedCso.data; + const selectedCso = this.selectAllocateCso?.selected as SelectOption; + if (selectedCso) { + const csoId = selectedCso.entityId; + const csoUsername = selectedCso.data; - this.attemptToAssignCsoToSelectedHearings(csoId, csoUsername); - } - } else { - this.allocationHearingViewModel.uncheckHearingAndRevert(hearing_id); + this.attemptToAssignCsoToSelectedHearings(csoId, csoUsername); } } + deselectHearing(hearing_id: string) { + this.allocationHearingViewModel.uncheckHearingAndRevert(hearing_id); + } + getConcurrentCountText(count: number): string { return `User has ${count} concurrent ${count > 1 ? 'hearings' : 'hearing'} allocated`; } diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/allocate-hearings/models/allocate-hearing.model.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/allocate-hearings/models/allocate-hearing.model.ts index 9e3bebf4b..9866e346e 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/allocate-hearings/models/allocate-hearing.model.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/allocate-hearings/models/allocate-hearing.model.ts @@ -84,8 +84,8 @@ export class AllocateHearingModel { } get hasPendingChanges(): boolean { - const original = this.originalState.map(h => { id: h.hearing_id, cso: h.allocated_cso }).sort(); - const current = this.hearings.map(h => { id: h.hearingId, cso: h.allocatedOfficerUsername }).sort(); + const original = this.originalState.map(h => { id: h.hearing_id, cso: h.allocated_cso }).sort((a, b) => a - b); + const current = this.hearings.map(h => { id: h.hearingId, cso: h.allocatedOfficerUsername }).sort((a, b) => a - b); const stringMatch = JSON.stringify(original) === JSON.stringify(current); return !stringMatch; diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/edit-work-hours.component.spec.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/edit-work-hours.component.spec.ts index f0b722eba..b81b62f2e 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/edit-work-hours.component.spec.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/edit-work-hours.component.spec.ts @@ -31,7 +31,8 @@ describe('EditWorkHoursComponent', () => { beforeEach(async () => { videoServiceSpy = jasmine.createSpyObj('VideoHearingsService', [ 'cancelVhoNonAvailabiltiesRequest', - 'setVhoNonAvailabiltiesHaveChanged' + 'setVhoNonAvailabiltiesHaveChanged', + 'unsetVhoNonAvailabiltiesHaveChanged' ]); bHClientSpy = jasmine.createSpyObj('BHClient', ['uploadWorkHours', 'uploadNonWorkingHours', 'updateNonAvailabilityWorkHours']); bHClientSpy.uploadWorkHours.and.returnValue(of(new UploadWorkHoursResponse({ failed_usernames: [] }))); diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/vho-search/vho-search.component.spec.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/vho-search/vho-search.component.spec.ts index 985bd3fb7..ec5ca8839 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/vho-search/vho-search.component.spec.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/vho-search/vho-search.component.spec.ts @@ -250,4 +250,24 @@ describe('VhoSearchComponent', () => { expect(component.dataChange.emit).toHaveBeenCalledTimes(0); }); }); + + describe('dataChangedBroadcast events received', () => { + beforeEach(() => { + spyOn(component, 'cancelEditing'); + spyOn(component, 'handleContinue'); + component.ngOnInit(); + }); + + it('should call cancelEditing when dataChangedBroadcast emits true', () => { + component.dataChangedBroadcast.next(true); + expect(component.cancelEditing).toHaveBeenCalled(); + expect(component.handleContinue).not.toHaveBeenCalled(); + }); + + it('should call handleContinue when dataChangedBroadcast emits false', () => { + component.dataChangedBroadcast.next(false); + expect(component.handleContinue).toHaveBeenCalled(); + expect(component.cancelEditing).not.toHaveBeenCalled(); + }); + }); }); diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/vho-search/vho-search.component.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/vho-search/vho-search.component.ts index 5a5408e54..046be0c5d 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/vho-search/vho-search.component.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/vho-search/vho-search.component.ts @@ -46,18 +46,22 @@ export class VhoSearchComponent implements OnInit { username: ['', Validators.required], hoursType: ['', Validators.required] }); - this.dataChangedBroadcast.subscribe(x => { - if (!x) { - this.handleContinue(); - } else { - this.cancelEditing(); - } + this.dataChangedBroadcast.subscribe({ + next: (x: boolean) => (x ? this.handleDataChanged() : this.handleDataUnchanged()) }); this.service.fetchNonWorkHours$.subscribe(async refresh => { await this.search(refresh); }); } + handleDataChanged() { + this.cancelEditing(); + } + + handleDataUnchanged() { + this.handleContinue(); + } + async search(refresh: boolean = false): Promise { if (this.form.valid) { const hoursType: HoursType = this.form.controls['hoursType'].value; diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/vho-work-hours-non-availability-table/vho-work-hours-non-availability-table.component.spec.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/vho-work-hours-non-availability-table/vho-work-hours-non-availability-table.component.spec.ts index 9fd1814d6..48ef9f6d4 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/vho-work-hours-non-availability-table/vho-work-hours-non-availability-table.component.spec.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/vho-work-hours-non-availability-table/vho-work-hours-non-availability-table.component.spec.ts @@ -20,7 +20,8 @@ describe('VhoNonAvailabilityWorkHoursTableComponent', () => { let loggerSpy: jasmine.SpyObj; const videoServiceSpy = jasmine.createSpyObj('VideoHearingsService', [ 'cancelVhoNonAvailabiltiesRequest', - 'setVhoNonAvailabiltiesHaveChanged' + 'setVhoNonAvailabiltiesHaveChanged', + 'unsetVhoNonAvailabiltiesHaveChanged' ]); beforeEach(async () => { diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/vho-work-hours-non-availability-table/vho-work-hours-non-availability-table.component.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/vho-work-hours-non-availability-table/vho-work-hours-non-availability-table.component.ts index 674006803..eb43f0978 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/vho-work-hours-non-availability-table/vho-work-hours-non-availability-table.component.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/vho-work-hours-non-availability-table/vho-work-hours-non-availability-table.component.ts @@ -179,7 +179,7 @@ export class VhoWorkHoursNonAvailabilityTableComponent implements OnInit, CanDea } registerUnsavedChanges() { - this.videoHearingsService.setVhoNonAvailabiltiesHaveChanged(true); + this.videoHearingsService.setVhoNonAvailabiltiesHaveChanged(); } validateNonWorkHour(nonWorkHour: EditVhoNonAvailabilityWorkHoursModel) { diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/vho-work-hours-table/vho-work-hours-table.component.spec.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/vho-work-hours-table/vho-work-hours-table.component.spec.ts index d13a58202..944d8c53b 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/vho-work-hours-table/vho-work-hours-table.component.spec.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/vho-work-hours-table/vho-work-hours-table.component.spec.ts @@ -11,7 +11,8 @@ describe('VhoWorkHoursTableComponent', () => { let fixture: ComponentFixture; const videoServiceSpy = jasmine.createSpyObj('VideoHearingsService', [ 'cancelVhoNonAvailabiltiesRequest', - 'setVhoNonAvailabiltiesHaveChanged' + 'setVhoNonAvailabiltiesHaveChanged', + 'unsetVhoNonAvailabiltiesHaveChanged' ]); beforeEach(async () => { diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/vho-work-hours-table/vho-work-hours-table.component.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/vho-work-hours-table/vho-work-hours-table.component.ts index 20c391ee7..864d4c7c3 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/vho-work-hours-table/vho-work-hours-table.component.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/work-allocation/edit-work-hours/vho-work-hours-table/vho-work-hours-table.component.ts @@ -165,7 +165,7 @@ export class VhoWorkHoursTableComponent implements CanDeactiveComponent { } registerUnsavedChanges() { - this.videoHearingsService.setVhoNonAvailabiltiesHaveChanged(true); + this.videoHearingsService.setVhoNonAvailabiltiesHaveChanged(); } get checkVhoHasWorkHours(): boolean { diff --git a/AdminWebsite/AdminWebsite/Configuration/FeatureToggles.cs b/AdminWebsite/AdminWebsite/Configuration/FeatureToggles.cs index 670c4f039..3b4644a45 100644 --- a/AdminWebsite/AdminWebsite/Configuration/FeatureToggles.cs +++ b/AdminWebsite/AdminWebsite/Configuration/FeatureToggles.cs @@ -13,7 +13,7 @@ public interface IFeatureToggles public class FeatureToggles : IFeatureToggles { - private readonly ILdClient _ldClient; + private readonly LdClient _ldClient; private readonly Context _context; private const string LdUser = "vh-admin-web"; private const string Dom1EnabledToggleKey = "dom1"; diff --git a/AdminWebsite/AdminWebsite/Contracts/Requests/BookHearingRequest.cs b/AdminWebsite/AdminWebsite/Contracts/Requests/BookHearingRequest.cs index 339ad812e..8719ce112 100644 --- a/AdminWebsite/AdminWebsite/Contracts/Requests/BookHearingRequest.cs +++ b/AdminWebsite/AdminWebsite/Contracts/Requests/BookHearingRequest.cs @@ -1,3 +1,4 @@ +using System.Text.Json.Serialization; using AdminWebsite.Models; namespace AdminWebsite.Contracts.Requests @@ -5,7 +6,10 @@ namespace AdminWebsite.Contracts.Requests public class BookHearingRequest { public BookingDetailsRequest BookingDetails { get; set; } + + [JsonRequired] public bool IsMultiDay { get; set; } + public MultiHearingRequest MultiHearingDetails { get; set; } public string OtherInformationDetails { get; set; } } diff --git a/AdminWebsite/AdminWebsite/Contracts/Requests/BookingDetailsRequest.cs b/AdminWebsite/AdminWebsite/Contracts/Requests/BookingDetailsRequest.cs index 5927bd8d0..968ed8690 100644 --- a/AdminWebsite/AdminWebsite/Contracts/Requests/BookingDetailsRequest.cs +++ b/AdminWebsite/AdminWebsite/Contracts/Requests/BookingDetailsRequest.cs @@ -1,14 +1,19 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Text.Json.Serialization; using AdminWebsite.Contracts.Enums; namespace AdminWebsite.Contracts.Requests; public class BookingDetailsRequest { + [JsonRequired] public DateTime ScheduledDateTime { get; set; } + + [JsonRequired] public int ScheduledDuration { get; set; } + public string HearingVenueCode { get; set; } public string CaseTypeName { get; set; } public string CaseTypeServiceId { get; set; } @@ -18,10 +23,17 @@ public class BookingDetailsRequest public string HearingRoomName { get; set; } public string OtherInformation { get; set; } public string CreatedBy { get; set; } + + [JsonRequired] public bool AudioRecordingRequired { get; set; } + [DefaultValue(false)] + [JsonRequired] public bool IsMultiDayHearing { get; set; } + + [JsonRequired] public VideoSupplier ConferenceSupplier { get; set; } + public List Endpoints { get; set; } public List LinkedParticipants { get; set; } } \ No newline at end of file diff --git a/AdminWebsite/AdminWebsite/Contracts/Requests/BookingSearchRequest.cs b/AdminWebsite/AdminWebsite/Contracts/Requests/BookingSearchRequest.cs index 0571239dc..87ba89e2c 100644 --- a/AdminWebsite/AdminWebsite/Contracts/Requests/BookingSearchRequest.cs +++ b/AdminWebsite/AdminWebsite/Contracts/Requests/BookingSearchRequest.cs @@ -45,10 +45,12 @@ public BookingSearchRequest() [JsonProperty("noJudge")] [Required] + [JsonRequired] public bool Nojudge { get; set; } [JsonProperty("noAllocated")] [Required] + [JsonRequired] public bool NoAllocated { get; set; } } } diff --git a/AdminWebsite/AdminWebsite/Contracts/Requests/CancelMultiDayHearingRequest.cs b/AdminWebsite/AdminWebsite/Contracts/Requests/CancelMultiDayHearingRequest.cs index c26d1aed0..434255f50 100644 --- a/AdminWebsite/AdminWebsite/Contracts/Requests/CancelMultiDayHearingRequest.cs +++ b/AdminWebsite/AdminWebsite/Contracts/Requests/CancelMultiDayHearingRequest.cs @@ -1,3 +1,5 @@ +using System.Text.Json.Serialization; + namespace AdminWebsite.Contracts.Requests { public class CancelMultiDayHearingRequest @@ -5,6 +7,7 @@ public class CancelMultiDayHearingRequest /// /// When true, applies updates to future days of the multi day hearing as well /// + [JsonRequired] public bool UpdateFutureDays { get; set; } /// diff --git a/AdminWebsite/AdminWebsite/Contracts/Requests/EditMultiDayHearingRequest.cs b/AdminWebsite/AdminWebsite/Contracts/Requests/EditMultiDayHearingRequest.cs index a1c89d4a8..d3b87034f 100644 --- a/AdminWebsite/AdminWebsite/Contracts/Requests/EditMultiDayHearingRequest.cs +++ b/AdminWebsite/AdminWebsite/Contracts/Requests/EditMultiDayHearingRequest.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Text.Json.Serialization; using AdminWebsite.Models; namespace AdminWebsite.Contracts.Requests @@ -16,6 +17,7 @@ public EditMultiDayHearingRequest() /// /// Duration of the hearing /// + [JsonRequired] public int ScheduledDuration { get; set; } /// @@ -46,6 +48,7 @@ public EditMultiDayHearingRequest() /// /// Gets or sets the audio recording required flag, value true is indicated that recording is required, otherwise false /// + [JsonRequired] public bool AudioRecordingRequired { get; set; } /// @@ -71,6 +74,7 @@ public EditMultiDayHearingRequest() /// /// When true, applies updates to future days of the multi day hearing as well /// + [JsonRequired] public bool UpdateFutureDays { get; set; } } } diff --git a/AdminWebsite/AdminWebsite/Controllers/BookingListController.cs b/AdminWebsite/AdminWebsite/Controllers/BookingListController.cs index 6c1be340a..bfc33ed55 100644 --- a/AdminWebsite/AdminWebsite/Controllers/BookingListController.cs +++ b/AdminWebsite/AdminWebsite/Controllers/BookingListController.cs @@ -106,7 +106,7 @@ private async Task> GetCaseTypesId(IEnumerable caseTypes) { var typeIds = new List(); var types = await _bookingsApiClient.GetCaseTypesAsync(includeDeleted:true); - if (types != null && types.Any()) + if (types != null && types.Count != 0) foreach (var item in caseTypes) { var caseType = types.FirstOrDefault(s => s.Name == item); diff --git a/AdminWebsite/AdminWebsite/Controllers/HearingsController.cs b/AdminWebsite/AdminWebsite/Controllers/HearingsController.cs index d71711853..62c59ce88 100644 --- a/AdminWebsite/AdminWebsite/Controllers/HearingsController.cs +++ b/AdminWebsite/AdminWebsite/Controllers/HearingsController.cs @@ -85,7 +85,7 @@ public async Task> Post([FromBody] BookHear newBookingRequest.IsMultiDayHearing = request.IsMultiDay; try { - if (newBookingRequest.Endpoints != null && newBookingRequest.Endpoints.Any()) + if (newBookingRequest.Endpoints != null && newBookingRequest.Endpoints.Count != 0) { var endpointsWithDa = newBookingRequest.Endpoints .Where(x => !string.IsNullOrWhiteSpace(x.DefenceAdvocateContactEmail)) @@ -174,7 +174,7 @@ public async Task CloneHearing(Guid hearingId, MultiHearingReques var hearingDates = GetDatesForClonedHearings(hearingRequest); - if (!hearingDates.Any()) + if (hearingDates.Count == 0) { _logger.LogWarning("No working dates provided to clone to"); return BadRequest(); @@ -547,7 +547,7 @@ private async Task UpdateParticipantsAndEndpointsV2(Guid hearingId, List(request.NewParticipants)); @@ -590,7 +590,7 @@ private static List GetRemovedParticipantIds(List .Select(x => x.Id).ToList(); } - private static IEnumerable GetRemovedParticipants(List participants, HearingDetailsResponse originalHearing) + private static List GetRemovedParticipants(List participants, HearingDetailsResponse originalHearing) { return originalHearing.Participants.Where(p => participants.TrueForAll(rp => rp.Id != p.Id)) .Select(x => x).ToList(); @@ -640,7 +640,7 @@ private async Task UpdateJudiciaryParticipants(Guid hearingId, List jp.HearingRoleCode != JudiciaryParticipantHearingRoleCode.Judge) .ToList(); - if (johsToAdd.Any()) + if (johsToAdd.Count != 0) { await _bookingsApiClient.AddJudiciaryParticipantsToHearingAsync(hearingId, johsToAdd); } @@ -712,12 +712,12 @@ private static UpdateJudiciaryParticipantsRequest MapUpdateJudiciaryParticipants InterpreterLanguageCode = jp.InterpreterLanguageCode }; }).ToList(); - if (newJohRequest.Any()) + if (newJohRequest.Count != 0) { var johsToAdd = newJohRequest .ToList(); - if (johsToAdd.Any()) + if (johsToAdd.Count != 0) { var newParticipants = johsToAdd .Select(x => new BookingsApi.Contract.V1.Requests.JudiciaryParticipantRequest @@ -940,7 +940,7 @@ public async Task GetHearingConferenceStatus(Guid hearingId) var errorMessage = $"Failed to get the booking created status, possibly the conference was not created - hearingId: {hearingId}"; try { - _logger.LogDebug("Hearing {1} is booked. Polling for the status in BookingsApi", hearingId); + _logger.LogDebug("Hearing {HearingId} is booked. Polling for the status in BookingsApi", hearingId); var response = await GetHearing(hearingId); var participantsNeedVhAccounts = ParticipantsNeedVhAccounts(response.Participants); var accountsStillNeedCreating = participantsNeedVhAccounts.Any(x => x.ContactEmail == x.Username); @@ -1043,14 +1043,13 @@ public async Task CancelBooking(Guid hearingId, string reason) [ProducesResponseType((int) HttpStatusCode.BadRequest)] public async Task UpdateFailedBookingStatus(Guid hearingId) { - var errorMessage = $"Failed to update the failed status for a hearing - hearingId: {hearingId}"; try { await _bookingsApiClient.FailBookingAsync(hearingId); } catch (VideoApiException e) { - _logger.LogError(e, errorMessage); + _logger.LogError(e, "Failed to update the failed status for a hearing - hearingId: {HearingId}", hearingId); if (e.StatusCode == (int) HttpStatusCode.NotFound) return NotFound(); if (e.StatusCode == (int) HttpStatusCode.BadRequest) return BadRequest(e.Response); } diff --git a/AdminWebsite/AdminWebsite/Controllers/WorkAllocationController.cs b/AdminWebsite/AdminWebsite/Controllers/WorkAllocationController.cs index 818f6f1f4..77060c49a 100644 --- a/AdminWebsite/AdminWebsite/Controllers/WorkAllocationController.cs +++ b/AdminWebsite/AdminWebsite/Controllers/WorkAllocationController.cs @@ -39,7 +39,7 @@ public async Task GetUnallocatedHearings() { var unallocatedHearings = await _bookingsApiClient.GetUnallocatedHearingsAsync(); - if (unallocatedHearings == null || !unallocatedHearings.Any()) + if (unallocatedHearings == null || unallocatedHearings.Count == 0) return Ok(UnallocatedHearingsForVhoMapper.MapFrom(new List(), DateTime.Today)); return Ok(UnallocatedHearingsForVhoMapper.MapFrom(unallocatedHearings.ToList(), DateTime.Today)); @@ -59,7 +59,7 @@ public async Task GetAllocationHearings( cso: searchRequest.Cso, isUnallocated: searchRequest.IsUnallocated); - if (hearings == null || !hearings.Any()) + if (hearings == null || hearings.Count == 0) return Ok(new List()); return Ok(hearings.Select(AllocationHearingsResponseMapper.Map)); @@ -79,7 +79,7 @@ public async Task AllocateHearingsToCso(UpdateHearingAllocationTo { var hearings = await _bookingsApiClient.AllocateHearingsToCsoAsync(request); - if (hearings == null || !hearings.Any()) + if (hearings == null || hearings.Count == 0) return Ok(new List()); return Ok(hearings.Select(AllocationHearingsResponseMapper.Map).ToList()); diff --git a/AdminWebsite/AdminWebsite/Dockerfile b/AdminWebsite/AdminWebsite/Dockerfile index 9bc4c9a41..64efaede3 100644 --- a/AdminWebsite/AdminWebsite/Dockerfile +++ b/AdminWebsite/AdminWebsite/Dockerfile @@ -8,8 +8,11 @@ FROM node:18.13-alpine AS client ARG skip_client_build=false WORKDIR /app COPY AdminWebsite/ClientApp . -RUN [[ ${skip_client_build} = true ]] && echo "Skipping npm install" || npm install -RUN [[ ${skip_client_build} = true ]] && mkdir dist || npm run-script build-prod +RUN if [ "$skip_client_build" = "true" ]; then \ + echo "Skipping npm install" && mkdir dist; \ + else \ + npm install && npm run-script build-prod; \ + fi FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /src diff --git a/AdminWebsite/AdminWebsite/Mappers/EditMultiDayHearing/HearingChangesMapper.cs b/AdminWebsite/AdminWebsite/Mappers/EditMultiDayHearing/HearingChangesMapper.cs index 8547cc22a..4609d27f3 100644 --- a/AdminWebsite/AdminWebsite/Mappers/EditMultiDayHearing/HearingChangesMapper.cs +++ b/AdminWebsite/AdminWebsite/Mappers/EditMultiDayHearing/HearingChangesMapper.cs @@ -153,7 +153,7 @@ private static LinkedParticipantChanges MapLinkedParticipantChanges( return linkedParticipantChanges; } - private static IEnumerable GetRemovedParticipants(List participants, HearingDetailsResponse originalHearing) + private static List GetRemovedParticipants(List participants, HearingDetailsResponse originalHearing) { return originalHearing.Participants .Where(p => participants.TrueForAll(rp => rp.Id != p.Id)) @@ -186,7 +186,7 @@ private static List MapEndpointChanges(HearingDetailsResponseV2 return endpointChanges; } - private static IEnumerable GetRemovedEndpoints(List endpoints, HearingDetailsResponse originalHearing) + private static List GetRemovedEndpoints(List endpoints, HearingDetailsResponse originalHearing) { return originalHearing.Endpoints .Where(p => endpoints.TrueForAll(rp => rp.Id != p.Id)) @@ -194,14 +194,14 @@ private static IEnumerable GetRemovedEndpoints(List GetNewJudiciaryParticipants(HearingDetailsResponseV2 hearing, EditMultiDayHearingRequest request) + private static List GetNewJudiciaryParticipants(HearingDetailsResponseV2 hearing, EditMultiDayHearingRequest request) { return request.JudiciaryParticipants .Where(rjp => !hearing.JudiciaryParticipants.Exists(jp => jp.PersonalCode == rjp.PersonalCode)) .ToList(); } - private static IEnumerable GetRemovedJudiciaryParticipants( + private static List GetRemovedJudiciaryParticipants( List judiciaryParticipants, HearingDetailsResponse originalHearing) { diff --git a/AdminWebsite/AdminWebsite/Mappers/EditMultiDayHearing/ParticipantIdMapper.cs b/AdminWebsite/AdminWebsite/Mappers/EditMultiDayHearing/ParticipantIdMapper.cs index 7d12f3dca..9246069ac 100644 --- a/AdminWebsite/AdminWebsite/Mappers/EditMultiDayHearing/ParticipantIdMapper.cs +++ b/AdminWebsite/AdminWebsite/Mappers/EditMultiDayHearing/ParticipantIdMapper.cs @@ -43,8 +43,8 @@ public static void AssignParticipantIdsForFutureDayHearing( private static void CreateParticipantMappings( HearingDetailsResponse multiDayHearingFutureDay, List participants, - IDictionary participantIdMappings, - IDictionary participantsNewToEditedHearingButExistOnFutureDayHearing) + Dictionary participantIdMappings, + Dictionary participantsNewToEditedHearingButExistOnFutureDayHearing) { foreach (var participant in participants) { @@ -67,8 +67,8 @@ private static void CreateParticipantMappings( private static void MapParticipantId( EditParticipantRequest participant, - IReadOnlyDictionary participantIdMappings, - IReadOnlyDictionary participantsNewToEditedHearingButExistOnFutureDayHearing) + Dictionary participantIdMappings, + Dictionary participantsNewToEditedHearingButExistOnFutureDayHearing) { if (participant.Id.HasValue) { diff --git a/AdminWebsite/AdminWebsite/Mappers/HearingDetailsResponseMapper.cs b/AdminWebsite/AdminWebsite/Mappers/HearingDetailsResponseMapper.cs index 8178f6797..f37225345 100644 --- a/AdminWebsite/AdminWebsite/Mappers/HearingDetailsResponseMapper.cs +++ b/AdminWebsite/AdminWebsite/Mappers/HearingDetailsResponseMapper.cs @@ -50,13 +50,13 @@ public static HearingDetailsResponse Map(this V2.HearingDetailsResponseV2 hearin public static HearingDetailsResponse Map(this V2.HearingDetailsResponseV2 hearingDetails, ICollection hearingsInGroup) { var response = hearingDetails.Map(); - if (hearingsInGroup == null || !hearingsInGroup.Any()) return response; + if (hearingsInGroup == null || hearingsInGroup.Count == 0) return response; var activeHearings = hearingsInGroup .Where(h => h.Status != BookingStatusV2.Cancelled && h.Status != BookingStatusV2.Failed) .ToList(); - if (activeHearings.Any()) + if (activeHearings.Count != 0) { response.MultiDayHearingLastDayScheduledDateTime = activeHearings.ScheduledDateTimeOfLastHearing(); } diff --git a/AdminWebsite/AdminWebsite/Models/EditHearingRequest.cs b/AdminWebsite/AdminWebsite/Models/EditHearingRequest.cs index 9f5dfb415..c640195cd 100644 --- a/AdminWebsite/AdminWebsite/Models/EditHearingRequest.cs +++ b/AdminWebsite/AdminWebsite/Models/EditHearingRequest.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Text.Json.Serialization; using AdminWebsite.Contracts.Requests; namespace AdminWebsite.Models @@ -19,11 +20,13 @@ public EditHearingRequest() /// /// The date and time for a hearing /// + [JsonRequired] public DateTime ScheduledDateTime { get; set; } /// /// The duration of a hearing (number of minutes) /// + [JsonRequired] public int ScheduledDuration { get; set; } /// @@ -64,6 +67,7 @@ public EditHearingRequest() /// /// Gets or sets audio recording required flag /// + [JsonRequired] public bool AudioRecordingRequired { get; set; } /// diff --git a/AdminWebsite/AdminWebsite/Models/MultiHearingRequest.cs b/AdminWebsite/AdminWebsite/Models/MultiHearingRequest.cs index 6f2d54594..f0199457a 100644 --- a/AdminWebsite/AdminWebsite/Models/MultiHearingRequest.cs +++ b/AdminWebsite/AdminWebsite/Models/MultiHearingRequest.cs @@ -1,16 +1,23 @@ using System; using System.Collections.Generic; +using System.Text.Json.Serialization; namespace AdminWebsite.Models { public class MultiHearingRequest { + [JsonRequired] public DateTime StartDate { get; set; } + + [JsonRequired] public DateTime EndDate { get; set; } public IList HearingDates { get; set; } + [JsonRequired] public bool IsIndividualDates { get; set; } + + [JsonRequired] public int ScheduledDuration { get; set; } } } diff --git a/AdminWebsite/AdminWebsite/Properties/ServiceDependencies/vh-admin-web-demo - Web Deploy/profile.arm.json b/AdminWebsite/AdminWebsite/Properties/ServiceDependencies/vh-admin-web-demo - Web Deploy/profile.arm.json deleted file mode 100644 index 3870aee9b..000000000 --- a/AdminWebsite/AdminWebsite/Properties/ServiceDependencies/vh-admin-web-demo - Web Deploy/profile.arm.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_dependencyType": "compute.appService.windows" - }, - "parameters": { - "resourceGroupName": { - "type": "string", - "defaultValue": "vh-admin-web-demo", - "metadata": { - "description": "Name of the resource group for the resource. It is recommended to put resources under same resource group for better tracking." - } - }, - "resourceGroupLocation": { - "type": "string", - "defaultValue": "ukwest", - "metadata": { - "description": "Location of the resource group. Resource groups could have different location than resources, however by default we use API versions from latest hybrid profile which support all locations for resource types we support." - } - }, - "resourceName": { - "type": "string", - "defaultValue": "vh-admin-web-demo", - "metadata": { - "description": "Name of the main resource to be created by this template." - } - }, - "resourceLocation": { - "type": "string", - "defaultValue": "[parameters('resourceGroupLocation')]", - "metadata": { - "description": "Location of the resource. By default use resource group's location, unless the resource provider is not supported there." - } - } - }, - "variables": { - "appServicePlan_name": "[concat('Plan', uniqueString(concat(parameters('resourceName'), subscription().subscriptionId)))]", - "appServicePlan_ResourceId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('resourceGroupName'), '/providers/Microsoft.Web/serverFarms/', variables('appServicePlan_name'))]" - }, - "resources": [ - { - "type": "Microsoft.Resources/resourceGroups", - "name": "[parameters('resourceGroupName')]", - "location": "[parameters('resourceGroupLocation')]", - "apiVersion": "2019-10-01" - }, - { - "type": "Microsoft.Resources/deployments", - "name": "[concat(parameters('resourceGroupName'), 'Deployment', uniqueString(concat(parameters('resourceName'), subscription().subscriptionId)))]", - "resourceGroup": "[parameters('resourceGroupName')]", - "apiVersion": "2019-10-01", - "dependsOn": [ - "[parameters('resourceGroupName')]" - ], - "properties": { - "mode": "Incremental", - "template": { - "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "resources": [ - { - "location": "[parameters('resourceLocation')]", - "name": "[parameters('resourceName')]", - "type": "Microsoft.Web/sites", - "apiVersion": "2015-08-01", - "tags": { - "[concat('hidden-related:', variables('appServicePlan_ResourceId'))]": "empty" - }, - "dependsOn": [ - "[variables('appServicePlan_ResourceId')]" - ], - "kind": "app", - "properties": { - "name": "[parameters('resourceName')]", - "kind": "app", - "httpsOnly": true, - "reserved": false, - "serverFarmId": "[variables('appServicePlan_ResourceId')]", - "siteConfig": { - "metadata": [ - { - "name": "CURRENT_STACK", - "value": "dotnetcore" - } - ] - } - }, - "identity": { - "type": "SystemAssigned" - } - }, - { - "location": "[parameters('resourceLocation')]", - "name": "[variables('appServicePlan_name')]", - "type": "Microsoft.Web/serverFarms", - "apiVersion": "2015-08-01", - "sku": { - "name": "S1", - "tier": "Standard", - "family": "S", - "size": "S1" - }, - "properties": { - "name": "[variables('appServicePlan_name')]" - } - } - ] - } - } - } - ] -} \ No newline at end of file diff --git a/AdminWebsite/AdminWebsite/Services/ApplicationLogger.cs b/AdminWebsite/AdminWebsite/Services/ApplicationLogger.cs index 45fa3677c..238daae36 100644 --- a/AdminWebsite/AdminWebsite/Services/ApplicationLogger.cs +++ b/AdminWebsite/AdminWebsite/Services/ApplicationLogger.cs @@ -70,10 +70,7 @@ public static void TraceWithObject(string traceCategory, string eventTitle, stri public static void TraceException(string traceCategory, string eventTitle, Exception exception, IPrincipal user, IDictionary properties) { - if (exception == null) - { - throw new ArgumentNullException(nameof(exception)); - } + ArgumentNullException.ThrowIfNull(exception); var exceptionTelemetry = new ExceptionTelemetry(exception); diff --git a/AdminWebsite/AdminWebsite/Services/UserAccountService.cs b/AdminWebsite/AdminWebsite/Services/UserAccountService.cs index 838797d3b..7152b1053 100644 --- a/AdminWebsite/AdminWebsite/Services/UserAccountService.cs +++ b/AdminWebsite/AdminWebsite/Services/UserAccountService.cs @@ -95,7 +95,10 @@ public UserAccountService( public async Task GetUserRoleAsync(string userName) { var user = await _userApiClient.GetUserByAdUserNameAsync(userName); - Enum.TryParse(user.UserRole, out var userRoleResult); + if (!Enum.TryParse(user.UserRole, out var userRoleResult)) + { + throw new InvalidOperationException($"Invalid user role: {user.UserRole}"); + } return new UserRole { UserRoleType = userRoleResult, CaseTypes = user.CaseType }; } @@ -222,7 +225,10 @@ private async Task CheckUsernameExistsInAdAsync(string username) try { var person = await _userApiClient.GetUserByAdUserNameAsync(username); - Enum.TryParse(person.UserRole, out var userRoleResult); + if (!Enum.TryParse(person.UserRole, out var userRoleResult)) + { + throw new InvalidOperationException($"Invalid user role: {person.UserRole}"); + } if (userRoleResult == UserRoleType.Judge || userRoleResult == UserRoleType.VhOfficer) { var e = new UserServiceException