diff --git a/.prettierrc b/.prettierrc index b382ff0a..480073c0 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,6 +1,7 @@ { "singleQuote": true, - "trailingComma": "none", + "trailingComma": "all", + "printWidth": 100, "objectWrap": "collapse", "semi": true, "useTabs": false, diff --git a/materials_devops_pipelines/Materials-PR.yml b/materials_devops_pipelines/Materials-PR.yml index 1c702af2..6b684348 100644 --- a/materials_devops_pipelines/Materials-PR.yml +++ b/materials_devops_pipelines/Materials-PR.yml @@ -44,7 +44,7 @@ stages: - stage: Wait_For_Running_PRs displayName: Wait for running PRs jobs: - - job: + - job: Query_Status pool: name: $(dev-build-agent) steps: @@ -61,7 +61,7 @@ stages: name: $(dev-build-agent) steps: - checkout: self - fetchDepth: 0 + fetchDepth: '0' - task: PowerShell@2 inputs: @@ -187,10 +187,10 @@ stages: steps: # Setup Node.js - - task: UseNode@1 + - task: NodeTool@0 displayName: "Use Node.js" inputs: - version: "$(nodeVersion)" + versionSpec: "$(nodeVersion)" - task: Npm@1 inputs: @@ -234,19 +234,19 @@ stages: - script: | npx playwright install - displayName: 'Install Playwright browsers' - condition: ne(variables['PLAYWRIGHT_CACHE_RESTORED'], 'true') - workingDirectory: '$(workingDir)' - env: - PLAYWRIGHT_BROWSERS_PATH: '$(Pipeline.Workspace)/.playwright' + displayName: 'Install Playwright browsers' + condition: ne(variables['PLAYWRIGHT_CACHE_RESTORED'], 'true') + workingDirectory: '$(workingDir)' + env: + PLAYWRIGHT_BROWSERS_PATH: '$(Pipeline.Workspace)/.playwright' - - script: | - npx playwright install-deps - displayName: 'Install Playwright OS dependencies' - condition: and(eq(variables['Agent.OS'], 'Linux'), ne(variables['PLAYWRIGHT_CACHE_RESTORED'], 'true')) - workingDirectory: '$(workingDir)' - env: - PLAYWRIGHT_BROWSERS_PATH: '$(Pipeline.Workspace)/.playwright' + - script: | + npx playwright install-deps + displayName: 'Install Playwright OS dependencies' + condition: and(eq(variables['Agent.OS'], 'Linux'), ne(variables['PLAYWRIGHT_CACHE_RESTORED'], 'true')) + workingDirectory: '$(workingDir)' + env: + PLAYWRIGHT_BROWSERS_PATH: '$(Pipeline.Workspace)/.playwright' - script: | npx playwright install msedge @@ -279,19 +279,19 @@ stages: VITE_GLOBAL_SCRIPT_URL: $(GLOBAL_SCRIPT_URL) VITE_E2E: True - - task: PublishPipelineArtifact@1 - displayName: 'Publish E2E Artifact' - inputs: - targetPath: '$(workingDir)/tests/playwright-report' - artifact: 'playwright-report' - publishLocation: 'pipeline' - condition: succeededOrFailed() + - task: PublishPipelineArtifact@1 + displayName: 'Publish E2E Artifact' + inputs: + targetPath: '$(workingDir)/tests/playwright-report' + artifact: 'playwright-report' + publishLocation: 'pipeline' + condition: succeededOrFailed() - - task: PublishTestResults@2 - displayName: 'Publish E2E Test Results (JUnit)' - inputs: - testResultsFormat: 'JUnit' - testResultsFiles: '$(workingDir)/tests/e2e-test-results.xml' - testRunTitle: 'E2E Tests' - publishRunAttachments: false - condition: always() \ No newline at end of file + - task: PublishTestResults@2 + displayName: 'Publish E2E Test Results (JUnit)' + inputs: + testResultsFormat: 'JUnit' + testResultsFiles: '$(workingDir)/tests/e2e-test-results.xml' + testRunTitle: 'E2E Tests' + publishRunAttachments: false + condition: always() \ No newline at end of file diff --git a/materials_devops_pipelines/Materials-UI-Build-Dev.yml b/materials_devops_pipelines/Materials-UI-Build-Dev.yml index aa76b78a..1f35a08c 100644 --- a/materials_devops_pipelines/Materials-UI-Build-Dev.yml +++ b/materials_devops_pipelines/Materials-UI-Build-Dev.yml @@ -26,6 +26,7 @@ variables: value: "materials_ui" - group: materials-global - group: materials-ui-spa-dev + - group: materials-kv-dev-terraform pool: name: $(dev-build-agent) @@ -94,6 +95,9 @@ stages: VITE_GLOBAL_SCRIPT_URL: $(GLOBAL_SCRIPT_URL) VITE_REDACTION_LOG_URL: $(VITE_REDACTION_LOG_URL) VITE_REDACTION_LOG_SCOPE: $(VITE_REDACTION_LOG_SCOPE) + VITE_APPLICATIONINSIGHTS_CONNECTION_STRING: $(innovation-development-app-insights-connection-string) + VITE_APPLICATIONINSIGHTS_SAMPLING_PERCENTAGE: $(APPLICATIONINSIGHTS_SAMPLING_PERCENTAGE) + VITE_APPLICATIONINSIGHTS_CLOUD_ROLE: $(APPLICATIONINSIGHTS_CLOUD_ROLE) - task: replacetokens@5 displayName: "Set environment specific CSP in serve.json" diff --git a/materials_devops_pipelines/Materials-UI-Build-Prod.yml b/materials_devops_pipelines/Materials-UI-Build-Prod.yml index 305e6f19..475a5218 100644 --- a/materials_devops_pipelines/Materials-UI-Build-Prod.yml +++ b/materials_devops_pipelines/Materials-UI-Build-Prod.yml @@ -26,6 +26,7 @@ variables: value: "materials_ui" - group: materials-global - group: materials-ui-spa-prod + - group: materials-kv-prod-terraform pool: name: $(prod-build-agent) @@ -94,7 +95,10 @@ stages: VITE_GLOBAL_SCRIPT_URL: $(GLOBAL_SCRIPT_URL) VITE_REDACTION_LOG_URL: $(VITE_REDACTION_LOG_URL) VITE_REDACTION_LOG_SCOPE: $(VITE_REDACTION_LOG_SCOPE) - + VITE_APPLICATIONINSIGHTS_CONNECTION_STRING: $(innovation-prod-app-insights-connection-string) + VITE_APPLICATIONINSIGHTS_SAMPLING_PERCENTAGE: $(APPLICATIONINSIGHTS_SAMPLING_PERCENTAGE) + VITE_APPLICATIONINSIGHTS_CLOUD_ROLE: $(APPLICATIONINSIGHTS_CLOUD_ROLE) + - task: replacetokens@5 displayName: "Set environment specific CSP in serve.json" inputs: diff --git a/materials_devops_pipelines/Materials-UI-Build-Staging.yml b/materials_devops_pipelines/Materials-UI-Build-Staging.yml index f4a63349..52524469 100644 --- a/materials_devops_pipelines/Materials-UI-Build-Staging.yml +++ b/materials_devops_pipelines/Materials-UI-Build-Staging.yml @@ -26,6 +26,7 @@ variables: value: "materials_ui" - group: materials-global - group: materials-ui-spa-stg + - group: materials-kv-stg-terraform pool: name: $(stg-build-agent) @@ -94,6 +95,9 @@ stages: VITE_GLOBAL_SCRIPT_URL: $(GLOBAL_SCRIPT_URL) VITE_REDACTION_LOG_URL: $(VITE_REDACTION_LOG_URL) VITE_REDACTION_LOG_SCOPE: $(VITE_REDACTION_LOG_SCOPE) + VITE_APPLICATIONINSIGHTS_CONNECTION_STRING: $(innovation-qa-app-insights-connection-string) + VITE_APPLICATIONINSIGHTS_SAMPLING_PERCENTAGE: $(APPLICATIONINSIGHTS_SAMPLING_PERCENTAGE) + VITE_APPLICATIONINSIGHTS_CLOUD_ROLE: $(APPLICATIONINSIGHTS_CLOUD_ROLE) - task: replacetokens@5 displayName: "Set environment specific CSP in serve.json" diff --git a/materials_ui/.env.example b/materials_ui/.env.example index 508e7a4e..6a5de5f0 100644 --- a/materials_ui/.env.example +++ b/materials_ui/.env.example @@ -8,6 +8,10 @@ VITE_POLARIS_GATEWAY_SCOPE= VITE_REDACTION_LOG_URL= VITE_REDACTION_LOG_SCOPE= +VITE_APPLICATIONINSIGHTS_CONNECTION_STRING= +VITE_APPLICATIONINSIGHTS_SAMPLING_PERCENTAGE= +VITE_APPLICATIONINSIGHTS_CLOUD_ROLE= + VITE_GLOBAL_SCRIPT_URL= VITE_DISABLE_FEATURE_FLAGS= diff --git a/materials_ui/.prettierrc b/materials_ui/.prettierrc index 70ff5d0d..0401b8f0 100644 --- a/materials_ui/.prettierrc +++ b/materials_ui/.prettierrc @@ -1,7 +1,8 @@ { "plugins": ["prettier-plugin-organize-imports"], "singleQuote": true, - "trailingComma": "none", + "trailingComma": "all", + "printWidth": 100, "objectWrap": "collapse", "semi": true, "useTabs": false, diff --git a/materials_ui/eslint.config.js b/materials_ui/eslint.config.js index a06d7cf4..33e47c3e 100644 --- a/materials_ui/eslint.config.js +++ b/materials_ui/eslint.config.js @@ -14,20 +14,13 @@ export default [ plugins: { 'react-hooks': reactHooks, 'react-refresh': reactRefresh }, rules: { ...reactHooks.configs.recommended.rules, - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true } - ], + 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }], 'react-hooks/exhaustive-deps': 'off', '@typescript-eslint/no-unused-vars': [ 'error', - { - argsIgnorePattern: '^_', - varsIgnorePattern: '^_', - caughtErrorsIgnorePattern: '^_' - } + { argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_' }, ], - '@typescript-eslint/no-explicit-any': 'warn' - } - } + '@typescript-eslint/no-explicit-any': 'warn', + }, + }, ]; diff --git a/materials_ui/package-lock.json b/materials_ui/package-lock.json index b2265af1..640fc3e6 100644 --- a/materials_ui/package-lock.json +++ b/materials_ui/package-lock.json @@ -24,7 +24,7 @@ "react": "^18.0.0", "react-dom": "^18.0.0", "react-hook-form": "^7.56.1", - "react-pdf": "^9.1.0", + "react-pdf": "^10.4.1", "react-router-dom": "^6.25.1", "react-use": "^17.6.0", "react-use-pagination": "^2.0.1", @@ -2949,6 +2949,256 @@ "node": ">=18" } }, + "node_modules/@napi-rs/canvas": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.100.tgz", + "integrity": "sha512-xglYA6q3XO5P3BNJYxVZ1IV7DLVjp1Py6nwag88YntrS+3vKHyYcMqXVS4ZztJmwz2uGvz1FWhI/4LgbR5uQDA==", + "license": "MIT", + "optional": true, + "workspaces": [ + "e2e/*" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@napi-rs/canvas-android-arm64": "0.1.100", + "@napi-rs/canvas-darwin-arm64": "0.1.100", + "@napi-rs/canvas-darwin-x64": "0.1.100", + "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.100", + "@napi-rs/canvas-linux-arm64-gnu": "0.1.100", + "@napi-rs/canvas-linux-arm64-musl": "0.1.100", + "@napi-rs/canvas-linux-riscv64-gnu": "0.1.100", + "@napi-rs/canvas-linux-x64-gnu": "0.1.100", + "@napi-rs/canvas-linux-x64-musl": "0.1.100", + "@napi-rs/canvas-win32-arm64-msvc": "0.1.100", + "@napi-rs/canvas-win32-x64-msvc": "0.1.100" + } + }, + "node_modules/@napi-rs/canvas-android-arm64": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.100.tgz", + "integrity": "sha512-hjhCKhntPv9+t4ckHymdx0phYNcVW+GKQR6Lzw2zE+pOVjOplSmtx9nNNknTjbEDLcuLZqA1y8ufKg1XfgftzQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-darwin-arm64": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.100.tgz", + "integrity": "sha512-2PcswRaC7Ly645DGt88///zuFDhJxJYdKAs1uU3mfk1atYkXufgcgLfBpk6Tm12nCQBaNt1wpybuPZ4qOhTo8A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-darwin-x64": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.100.tgz", + "integrity": "sha512-ePNZtj7pNIva/siZMg+HmbeozkIjqUIYdoymH8HaA3qK7LfzFN4WMBM8G6HQ9ZC+H3+Dnn5pqtiXpgLykaPOhw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.100.tgz", + "integrity": "sha512-d5cDB48oWFGU8/XPhUOFAlySgb/VAu7D+s8fi55K1Pcfg8aPplHWqMgibhVLU8ky7Pyg/fuiVLz4Nf3JrSTuUA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-gnu": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.100.tgz", + "integrity": "sha512-rDxgxRu69RvDlX/bh9o22DxLsGr8EqsNgotL9+RwQE1S0b0cqeatqsw6aW45mukm0B42DIAaAacKaYQ8cqS1nw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-musl": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.100.tgz", + "integrity": "sha512-K3mDW66N+xT2/V439u1alFANiBUjdEx2gLiNYnCmUsva5jZMxWTjafBYwTzYK+EMFMHrUoabuU+T1BIP5CgbYQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-riscv64-gnu": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.100.tgz", + "integrity": "sha512-mooqUBTIsccZpnoQC4NgrC1v6C1vof39etLNMnBwCY+p0gajWJvAHLGQ6g/gGyS5YrpDW+GefSN4+Cvcr08UWw==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-gnu": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.100.tgz", + "integrity": "sha512-1eCvkDCazm7FFhsT7DfGOdSaHgZVK3bt/dSBl5EWHOWmnz+I7j8tPseJqqD81NF+MH21jKUK4wQSDjN0mdhnTg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-musl": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.100.tgz", + "integrity": "sha512-20arT6lnI19S68qNlii73TSEDbECNgzMz2EpldC1V3mZFuRkeujXkcebRk0LRJe9SEUAooYiLokfMViY8IX7yA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-win32-arm64-msvc": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-arm64-msvc/-/canvas-win32-arm64-msvc-0.1.100.tgz", + "integrity": "sha512-DZFFT1wIAg37LJw37yhMRFfjATd3vTQzjZ1Yki8u2vhO6Hi5VE6BVaGQ1aaDu7xb4iMErz+9EOwjpS7xcxFeBw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-win32-x64-msvc": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.100.tgz", + "integrity": "sha512-MyT1j3mHC2+Lu4pBi9mKyMJhtP6U7k7EldY7sj/uS5gJA65gTXt8MefJQXLJo5d/vZbuWmfxzkEUNc/urV3pHA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, "node_modules/@nevware21/ts-async": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.5.4.tgz", @@ -4785,27 +5035,6 @@ "dev": true, "license": "MIT" }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "optional": true - }, "node_modules/baseline-browser-mapping": { "version": "2.9.2", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.2.tgz", @@ -4815,18 +5044,6 @@ "baseline-browser-mapping": "dist/cli.js" } }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "license": "MIT", - "optional": true, - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -4884,31 +5101,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "optional": true, - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, "node_modules/buffer-builder": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/buffer-builder/-/buffer-builder-0.2.0.tgz", @@ -4980,21 +5172,6 @@ ], "license": "CC-BY-4.0" }, - "node_modules/canvas": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/canvas/-/canvas-3.2.0.tgz", - "integrity": "sha512-jk0GxrLtUEmW/TmFsk2WghvgHe8B0pxGilqCL21y8lHkPUGa6FTsnCNtHPOzT8O3y+N+m3espawV80bbBlgfTA==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "dependencies": { - "node-addon-api": "^7.0.0", - "prebuild-install": "^7.1.3" - }, - "engines": { - "node": "^18.12.0 || >= 20.9.0" - } - }, "node_modules/chai": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", @@ -5055,13 +5232,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "license": "ISC", - "optional": true - }, "node_modules/cli-cursor": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", @@ -5432,22 +5602,6 @@ "dev": true, "license": "MIT" }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/deep-eql": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", @@ -5458,16 +5612,6 @@ "node": ">=6" } }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -5503,16 +5647,6 @@ "node": ">=6" } }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "license": "Apache-2.0", - "optional": true, - "engines": { - "node": ">=8" - } - }, "node_modules/diff": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", @@ -5590,16 +5724,6 @@ "dev": true, "license": "MIT" }, - "node_modules/end-of-stream": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "license": "MIT", - "optional": true, - "dependencies": { - "once": "^1.4.0" - } - }, "node_modules/entities": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", @@ -5949,16 +6073,6 @@ "dev": true, "license": "MIT" }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "license": "(MIT OR WTFPL)", - "optional": true, - "engines": { - "node": ">=6" - } - }, "node_modules/expect-type": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", @@ -6100,13 +6214,6 @@ "node": ">= 6" } }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "license": "MIT", - "optional": true - }, "node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -6214,13 +6321,6 @@ "node": ">= 0.4" } }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "license": "MIT", - "optional": true - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -6440,27 +6540,6 @@ "node": ">=0.10.0" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause", - "optional": true - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -6539,20 +6618,6 @@ "node": ">=8" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC", - "optional": true - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC", - "optional": true - }, "node_modules/inline-style-prefixer": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-7.0.1.tgz", @@ -6993,9 +7058,9 @@ } }, "node_modules/make-cancellable-promise": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/make-cancellable-promise/-/make-cancellable-promise-1.3.2.tgz", - "integrity": "sha512-GCXh3bq/WuMbS+Ky4JBPW1hYTOU+znU+Q5m9Pu+pI8EoUqIHk9+tviOKC6/qhHh8C4/As3tzJ69IF32kdz85ww==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/make-cancellable-promise/-/make-cancellable-promise-2.0.0.tgz", + "integrity": "sha512-3SEQqTpV9oqVsIWqAcmDuaNeo7yBO3tqPtqGRcKkEo0lrzD3wqbKG9mkxO65KoOgXqj+zH2phJ2LiAsdzlogSw==", "license": "MIT", "funding": { "url": "https://github.com/wojtekmaj/make-cancellable-promise?sponsor=1" @@ -7009,9 +7074,9 @@ "license": "ISC" }, "node_modules/make-event-props": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/make-event-props/-/make-event-props-1.6.2.tgz", - "integrity": "sha512-iDwf7mA03WPiR8QxvcVHmVWEPfMY1RZXerDVNCRYW7dUr2ppH3J58Rwb39/WG39yTZdRSxr3x+2v22tvI0VEvA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/make-event-props/-/make-event-props-2.0.0.tgz", + "integrity": "sha512-G/hncXrl4Qt7mauJEXSg3AcdYzmpkIITTNl5I+rH9sog5Yw0kK6vseJjCaPfOXqOqQuPUP89Rkhfz5kPS8ijtw==", "license": "MIT", "funding": { "url": "https://github.com/wojtekmaj/make-event-props?sponsor=1" @@ -7033,9 +7098,9 @@ "license": "CC0-1.0" }, "node_modules/merge-refs": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/merge-refs/-/merge-refs-1.3.0.tgz", - "integrity": "sha512-nqXPXbso+1dcKDpPCXvwZyJILz+vSLqGGOnDrYHQYE+B8n9JTCekVLC65AfCpR4ggVyA/45Y0iR9LDyS2iI+zA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-refs/-/merge-refs-2.0.0.tgz", + "integrity": "sha512-3+B21mYK2IqUWnd2EivABLT7ueDhb0b8/dGK8LoFQPrU61YITeCMn14F7y7qZafWNZhUEKb24cJdiT5Wxs3prg==", "license": "MIT", "funding": { "url": "https://github.com/wojtekmaj/merge-refs?sponsor=1" @@ -7097,19 +7162,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -7133,23 +7185,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "optional": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "license": "MIT", - "optional": true - }, "node_modules/moment": { "version": "2.30.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", @@ -7305,13 +7340,6 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/napi-build-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", - "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", - "license": "MIT", - "optional": true - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -7329,32 +7357,6 @@ "tslib": "^2.0.3" } }, - "node_modules/node-abi": { - "version": "3.85.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.85.0.tgz", - "integrity": "sha512-zsFhmbkAzwhTft6nd3VxcG0cvJsT70rL+BIGHWVq5fi6MwGrHwzqKaxXE+Hl2GmnGItnDKPPkO5/LQqjVkIdFg==", - "license": "MIT", - "optional": true, - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-abi/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "license": "ISC", - "optional": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/node-addon-api": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", @@ -7375,16 +7377,6 @@ "dev": true, "license": "MIT" }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "optional": true, - "dependencies": { - "wrappy": "1" - } - }, "node_modules/onetime": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", @@ -7562,16 +7554,6 @@ "node": ">=8" } }, - "node_modules/path2d": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/path2d/-/path2d-0.2.2.tgz", - "integrity": "sha512-+vnG6S4dYcYxZd+CZxzXCNKdELYZSKfohrk98yajCo1PtRoDgCTrrwOvK1GT0UoAdVszagDVllQc0U1vaX4NUQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=6" - } - }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", @@ -7590,16 +7572,15 @@ } }, "node_modules/pdfjs-dist": { - "version": "4.8.69", - "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-4.8.69.tgz", - "integrity": "sha512-IHZsA4T7YElCKNNXtiLgqScw4zPd3pG9do8UrznC757gMd7UPeHSL2qwNNMJo4r79fl8oj1Xx+1nh2YkzdMpLQ==", + "version": "5.4.296", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.4.296.tgz", + "integrity": "sha512-DlOzet0HO7OEnmUmB6wWGJrrdvbyJKftI1bhMitK7O2N8W2gc757yyYBbINy9IDafXAV9wmKr9t7xsTaNKRG5Q==", "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": ">=20.16.0 || >=22.3.0" }, "optionalDependencies": { - "canvas": "^3.0.0-rc2", - "path2d": "^0.2.1" + "@napi-rs/canvas": "^0.1.80" } }, "node_modules/picocolors": { @@ -7694,33 +7675,6 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/prebuild-install": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", - "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", - "license": "MIT", - "optional": true, - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^2.0.0", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -7800,17 +7754,6 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, - "node_modules/pump": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", - "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", - "license": "MIT", - "optional": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -7821,32 +7764,6 @@ "node": ">=6" } }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "optional": true, - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -7897,17 +7814,17 @@ "peer": true }, "node_modules/react-pdf": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-9.2.1.tgz", - "integrity": "sha512-AJt0lAIkItWEZRA5d/mO+Om4nPCuTiQ0saA+qItO967DTjmGjnhmF+Bi2tL286mOTfBlF5CyLzJ35KTMaDoH+A==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-10.4.1.tgz", + "integrity": "sha512-kS/35staVCBqS29verTQJQZXw7RfsRCPO3fdJoW1KXylcv7A9dw6DZ3vJXC2w+bIBgLw5FN4pOFvKSQtkQhPfA==", "license": "MIT", "dependencies": { "clsx": "^2.0.0", "dequal": "^2.0.3", - "make-cancellable-promise": "^1.3.1", - "make-event-props": "^1.6.0", - "merge-refs": "^1.3.0", - "pdfjs-dist": "4.8.69", + "make-cancellable-promise": "^2.0.0", + "make-event-props": "^2.0.0", + "merge-refs": "^2.0.0", + "pdfjs-dist": "5.4.296", "tiny-invariant": "^1.0.0", "warning": "^4.0.0" }, @@ -8011,21 +7928,6 @@ "react": "^16.8.0 || ^17 || ^18" } }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "optional": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -8310,27 +8212,6 @@ "tslib": "^2.1.0" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "optional": true - }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -8801,53 +8682,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "optional": true - }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "optional": true, - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, "node_modules/slice-ansi": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", @@ -8982,16 +8816,6 @@ "dev": true, "license": "MIT" }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "optional": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/string-argv": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", @@ -9188,36 +9012,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tar-fs": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", - "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/throttle-debounce": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-3.0.1.tgz", @@ -9466,19 +9260,6 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -9666,13 +9447,6 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT", - "optional": true - }, "node_modules/uuid": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", @@ -10196,13 +9970,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC", - "optional": true - }, "node_modules/ws": { "version": "8.18.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", diff --git a/materials_ui/package.json b/materials_ui/package.json index b313c04d..daf5b871 100644 --- a/materials_ui/package.json +++ b/materials_ui/package.json @@ -42,7 +42,7 @@ "react": "^18.0.0", "react-dom": "^18.0.0", "react-hook-form": "^7.56.1", - "react-pdf": "^9.1.0", + "react-pdf": "^10.4.1", "react-router-dom": "^6.25.1", "react-use": "^17.6.0", "react-use-pagination": "^2.0.1", diff --git a/materials_ui/playwright.config.ts b/materials_ui/playwright.config.ts index 79275299..28597aba 100644 --- a/materials_ui/playwright.config.ts +++ b/materials_ui/playwright.config.ts @@ -27,8 +27,8 @@ export default defineConfig({ workers: process.env.CI ? 1 : undefined, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ reporter: [ - ["html", { outputFolder: "./tests/playwright-report" }], - ["junit", { outputFile: "./tests/e2e-test-results.xml" }], + ['html', { outputFolder: './tests/playwright-report' }], + ['junit', { outputFile: './tests/e2e-test-results.xml' }], ], /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { @@ -38,10 +38,10 @@ export default defineConfig({ screenshot: 'only-on-failure', /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ trace: 'on-first-retry', - navigationTimeout: 60 * 1000, // 60s for page.goto() - actionTimeout: 30 * 1000, // 30s for clicks/fills + navigationTimeout: 60 * 1000, // 60s for page.goto() + actionTimeout: 30 * 1000, // 30s for clicks/fills }, - + /* Configure projects for major browsers */ projects: [ // { @@ -76,10 +76,10 @@ export default defineConfig({ use: { ...devices['Desktop Edge'], channel: 'msedge', - storageState: 'tests/.auth/globalSetup.json' + storageState: 'tests/.auth/globalSetup.json', }, - dependencies: ['setup'] - } + dependencies: ['setup'], + }, // { // name: 'Google Chrome', // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, @@ -95,7 +95,7 @@ export default defineConfig({ env: { ...process.env, // Keep E2E independent from external script availability in CI. - VITE_GLOBAL_SCRIPT_URL: '' - } - } -}); \ No newline at end of file + VITE_GLOBAL_SCRIPT_URL: '', + }, + }, +}); diff --git a/materials_ui/public/assets/images/manifest.json b/materials_ui/public/assets/images/manifest.json index 6c0dba9d..5eeab57f 100644 --- a/materials_ui/public/assets/images/manifest.json +++ b/materials_ui/public/assets/images/manifest.json @@ -1,12 +1,7 @@ { "icons": [ { "src": "images/favicon.ico", "type": "image/x-icon", "sizes": "48x48" }, - { - "src": "images/favicon.svg", - "type": "image/svg+xml", - "sizes": "150x150", - "purpose": "any" - }, + { "src": "images/favicon.svg", "type": "image/svg+xml", "sizes": "150x150", "purpose": "any" }, { "src": "images/govuk-icon-180.png", "type": "image/png", diff --git a/materials_ui/public/body-class.js b/materials_ui/public/body-class.js index e59a18b2..1bf6e93b 100644 --- a/materials_ui/public/body-class.js +++ b/materials_ui/public/body-class.js @@ -1,5 +1,2 @@ document.body.className += - ' js-enabled' + - ('noModule' in HTMLScriptElement.prototype - ? ' govuk-frontend-supported' - : ''); + ' js-enabled' + ('noModule' in HTMLScriptElement.prototype ? ' govuk-frontend-supported' : ''); diff --git a/materials_ui/public/global-components-msal-redirect.html b/materials_ui/public/global-components-msal-redirect.html index 779770e2..f47748a9 100644 --- a/materials_ui/public/global-components-msal-redirect.html +++ b/materials_ui/public/global-components-msal-redirect.html @@ -1,10 +1,9 @@ - + - - - Global components auth termination - - - - \ No newline at end of file +--> diff --git a/materials_ui/public/mockServiceWorker.js b/materials_ui/public/mockServiceWorker.js index 558540fa..08460145 100644 --- a/materials_ui/public/mockServiceWorker.js +++ b/materials_ui/public/mockServiceWorker.js @@ -7,114 +7,99 @@ * - Please do NOT modify this file. */ -const PACKAGE_VERSION = '2.12.4' -const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82' -const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') -const activeClientIds = new Set() +const PACKAGE_VERSION = '2.12.4'; +const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82'; +const IS_MOCKED_RESPONSE = Symbol('isMockedResponse'); +const activeClientIds = new Set(); addEventListener('install', function () { - self.skipWaiting() -}) + self.skipWaiting(); +}); addEventListener('activate', function (event) { - event.waitUntil(self.clients.claim()) -}) + event.waitUntil(self.clients.claim()); +}); addEventListener('message', async function (event) { - const clientId = Reflect.get(event.source || {}, 'id') + const clientId = Reflect.get(event.source || {}, 'id'); if (!clientId || !self.clients) { - return + return; } - const client = await self.clients.get(clientId) + const client = await self.clients.get(clientId); if (!client) { - return + return; } - const allClients = await self.clients.matchAll({ - type: 'window', - }) + const allClients = await self.clients.matchAll({ type: 'window' }); switch (event.data) { case 'KEEPALIVE_REQUEST': { - sendToClient(client, { - type: 'KEEPALIVE_RESPONSE', - }) - break + sendToClient(client, { type: 'KEEPALIVE_RESPONSE' }); + break; } case 'INTEGRITY_CHECK_REQUEST': { sendToClient(client, { type: 'INTEGRITY_CHECK_RESPONSE', - payload: { - packageVersion: PACKAGE_VERSION, - checksum: INTEGRITY_CHECKSUM, - }, - }) - break + payload: { packageVersion: PACKAGE_VERSION, checksum: INTEGRITY_CHECKSUM }, + }); + break; } case 'MOCK_ACTIVATE': { - activeClientIds.add(clientId) + activeClientIds.add(clientId); sendToClient(client, { type: 'MOCKING_ENABLED', - payload: { - client: { - id: client.id, - frameType: client.frameType, - }, - }, - }) - break + payload: { client: { id: client.id, frameType: client.frameType } }, + }); + break; } case 'CLIENT_CLOSED': { - activeClientIds.delete(clientId) + activeClientIds.delete(clientId); const remainingClients = allClients.filter((client) => { - return client.id !== clientId - }) + return client.id !== clientId; + }); // Unregister itself when there are no more clients if (remainingClients.length === 0) { - self.registration.unregister() + self.registration.unregister(); } - break + break; } } -}) +}); addEventListener('fetch', function (event) { - const requestInterceptedAt = Date.now() + const requestInterceptedAt = Date.now(); // Bypass navigation requests. if (event.request.mode === 'navigate') { - return + return; } // Opening the DevTools triggers the "only-if-cached" request // that cannot be handled by the worker. Bypass such requests. - if ( - event.request.cache === 'only-if-cached' && - event.request.mode !== 'same-origin' - ) { - return + if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin') { + return; } // Bypass all requests when there are no active clients. // Prevents the self-unregistered worked from handling requests // after it's been terminated (still remains active until the next reload). if (activeClientIds.size === 0) { - return + return; } - const requestId = crypto.randomUUID() - event.respondWith(handleRequest(event, requestId, requestInterceptedAt)) -}) + const requestId = crypto.randomUUID(); + event.respondWith(handleRequest(event, requestId, requestInterceptedAt)); +}); /** * @param {FetchEvent} event @@ -122,23 +107,18 @@ addEventListener('fetch', function (event) { * @param {number} requestInterceptedAt */ async function handleRequest(event, requestId, requestInterceptedAt) { - const client = await resolveMainClient(event) - const requestCloneForEvents = event.request.clone() - const response = await getResponse( - event, - client, - requestId, - requestInterceptedAt, - ) + const client = await resolveMainClient(event); + const requestCloneForEvents = event.request.clone(); + const response = await getResponse(event, client, requestId, requestInterceptedAt); // Send back the response clone for the "response:*" life-cycle events. // Ensure MSW is active and ready to handle the message, otherwise // this message will pend indefinitely. if (client && activeClientIds.has(client.id)) { - const serializedRequest = await serializeRequest(requestCloneForEvents) + const serializedRequest = await serializeRequest(requestCloneForEvents); // Clone the response so both the client and the library could consume it. - const responseClone = response.clone() + const responseClone = response.clone(); sendToClient( client, @@ -146,10 +126,7 @@ async function handleRequest(event, requestId, requestInterceptedAt) { type: 'RESPONSE', payload: { isMockedResponse: IS_MOCKED_RESPONSE in response, - request: { - id: requestId, - ...serializedRequest, - }, + request: { id: requestId, ...serializedRequest }, response: { type: responseClone.type, status: responseClone.status, @@ -160,10 +137,10 @@ async function handleRequest(event, requestId, requestInterceptedAt) { }, }, responseClone.body ? [serializedRequest.body, responseClone.body] : [], - ) + ); } - return response + return response; } /** @@ -175,30 +152,28 @@ async function handleRequest(event, requestId, requestInterceptedAt) { * @returns {Promise} */ async function resolveMainClient(event) { - const client = await self.clients.get(event.clientId) + const client = await self.clients.get(event.clientId); if (activeClientIds.has(event.clientId)) { - return client + return client; } if (client?.frameType === 'top-level') { - return client + return client; } - const allClients = await self.clients.matchAll({ - type: 'window', - }) + const allClients = await self.clients.matchAll({ type: 'window' }); return allClients .filter((client) => { // Get only those clients that are currently visible. - return client.visibilityState === 'visible' + return client.visibilityState === 'visible'; }) .find((client) => { // Find the client ID that's recorded in the // set of clients that have registered the worker. - return activeClientIds.has(client.id) - }) + return activeClientIds.has(client.id); + }); } /** @@ -211,36 +186,34 @@ async function resolveMainClient(event) { async function getResponse(event, client, requestId, requestInterceptedAt) { // Clone the request because it might've been already used // (i.e. its body has been read and sent to the client). - const requestClone = event.request.clone() + const requestClone = event.request.clone(); function passthrough() { // Cast the request headers to a new Headers instance // so the headers can be manipulated with. - const headers = new Headers(requestClone.headers) + const headers = new Headers(requestClone.headers); // Remove the "accept" header value that marked this request as passthrough. // This prevents request alteration and also keeps it compliant with the // user-defined CORS policies. - const acceptHeader = headers.get('accept') + const acceptHeader = headers.get('accept'); if (acceptHeader) { - const values = acceptHeader.split(',').map((value) => value.trim()) - const filteredValues = values.filter( - (value) => value !== 'msw/passthrough', - ) + const values = acceptHeader.split(',').map((value) => value.trim()); + const filteredValues = values.filter((value) => value !== 'msw/passthrough'); if (filteredValues.length > 0) { - headers.set('accept', filteredValues.join(', ')) + headers.set('accept', filteredValues.join(', ')); } else { - headers.delete('accept') + headers.delete('accept'); } } - return fetch(requestClone, { headers }) + return fetch(requestClone, { headers }); } // Bypass mocking when the client is not active. if (!client) { - return passthrough() + return passthrough(); } // Bypass initial page load requests (i.e. static assets). @@ -248,35 +221,31 @@ async function getResponse(event, client, requestId, requestInterceptedAt) { // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet // and is not ready to handle requests. if (!activeClientIds.has(client.id)) { - return passthrough() + return passthrough(); } // Notify the client that a request has been intercepted. - const serializedRequest = await serializeRequest(event.request) + const serializedRequest = await serializeRequest(event.request); const clientMessage = await sendToClient( client, { type: 'REQUEST', - payload: { - id: requestId, - interceptedAt: requestInterceptedAt, - ...serializedRequest, - }, + payload: { id: requestId, interceptedAt: requestInterceptedAt, ...serializedRequest }, }, [serializedRequest.body], - ) + ); switch (clientMessage.type) { case 'MOCK_RESPONSE': { - return respondWithMock(clientMessage.data) + return respondWithMock(clientMessage.data); } case 'PASSTHROUGH': { - return passthrough() + return passthrough(); } } - return passthrough() + return passthrough(); } /** @@ -287,21 +256,18 @@ async function getResponse(event, client, requestId, requestInterceptedAt) { */ function sendToClient(client, message, transferrables = []) { return new Promise((resolve, reject) => { - const channel = new MessageChannel() + const channel = new MessageChannel(); channel.port1.onmessage = (event) => { if (event.data && event.data.error) { - return reject(event.data.error) + return reject(event.data.error); } - resolve(event.data) - } + resolve(event.data); + }; - client.postMessage(message, [ - channel.port2, - ...transferrables.filter(Boolean), - ]) - }) + client.postMessage(message, [channel.port2, ...transferrables.filter(Boolean)]); + }); } /** @@ -314,17 +280,14 @@ function respondWithMock(response) { // instance will have status code set to 0. Since it's not possible to create // a Response instance with status code 0, handle that use-case separately. if (response.status === 0) { - return Response.error() + return Response.error(); } - const mockedResponse = new Response(response.body, response) + const mockedResponse = new Response(response.body, response); - Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { - value: true, - enumerable: true, - }) + Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { value: true, enumerable: true }); - return mockedResponse + return mockedResponse; } /** @@ -345,5 +308,5 @@ async function serializeRequest(request) { referrerPolicy: request.referrerPolicy, body: await request.arrayBuffer(), keepalive: request.keepalive, - } + }; } diff --git a/materials_ui/renovate.json b/materials_ui/renovate.json index 5db72dd6..22a99432 100644 --- a/materials_ui/renovate.json +++ b/materials_ui/renovate.json @@ -1,6 +1,4 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended" - ] + "extends": ["config:recommended"] } diff --git a/materials_ui/serve.json b/materials_ui/serve.json index 0d5bccff..f3aa1593 100644 --- a/materials_ui/serve.json +++ b/materials_ui/serve.json @@ -8,11 +8,8 @@ { "key": "Cache-Control", "value": "no-store" }, { "key": "X-Frame-Options", "value": "SAMEORIGIN" }, { "key": "X-Permitted-Cross-Domain-Policies", "value": "none" }, - { - "key": "Content-Security-Policy", - "value": "#{CSP_VALUE_IN_ADO_VARIABLE}#" - } + { "key": "Content-Security-Policy", "value": "#{CSP_VALUE_IN_ADO_VARIABLE}#" } ] } ] -} \ No newline at end of file +} diff --git a/materials_ui/src/__tests__/components/CaseSearch.test.tsx b/materials_ui/src/__tests__/components/CaseSearch.test.tsx index 7c9193e3..60bf89b6 100644 --- a/materials_ui/src/__tests__/components/CaseSearch.test.tsx +++ b/materials_ui/src/__tests__/components/CaseSearch.test.tsx @@ -8,7 +8,7 @@ describe('', () => { render( - + , ); expect(screen.getByText('Find a case')).toBeInTheDocument(); }); @@ -17,7 +17,7 @@ describe('', () => { render( - + , ); expect(screen.getByLabelText('Search for a case URN')).toBeInTheDocument(); }); @@ -26,7 +26,7 @@ describe('', () => { render( - + , ); expect(screen.getByRole('button', { name: 'Search' })).toBeInTheDocument(); }); @@ -35,10 +35,10 @@ describe('', () => { render( - + , ); expect( - screen.getByText('Search and review a CPS case in England and Wales') + screen.getByText('Search and review a CPS case in England and Wales'), ).toBeInTheDocument(); }); @@ -46,7 +46,7 @@ describe('', () => { render( - + , ); expect(screen.getByText('Cancel')).toBeInTheDocument(); }); @@ -55,7 +55,7 @@ describe('', () => { render( - + , ); const input = screen.getByLabelText('Search for a case URN'); @@ -64,8 +64,8 @@ describe('', () => { await userEvent.type(input, '06SC12345711'); await userEvent.click(button); - expect( - (await screen.findAllByText('Enter a URN in the right format')).length - ).toBeGreaterThan(0); + expect((await screen.findAllByText('Enter a URN in the right format')).length).toBeGreaterThan( + 0, + ); }); }); diff --git a/materials_ui/src/__tests__/stores/useCaseInfo.test.ts b/materials_ui/src/__tests__/stores/useCaseInfo.test.ts index c7543c38..eb4ce882 100644 --- a/materials_ui/src/__tests__/stores/useCaseInfo.test.ts +++ b/materials_ui/src/__tests__/stores/useCaseInfo.test.ts @@ -9,7 +9,7 @@ const caseInfoData1: CaseInfoType = { leadDefendantFirstNames: 'Joe', leadDefendantSurname: 'Bloggs', numberOfDefendants: 2, - unitName: 'Unit name 1' + unitName: 'Unit name 1', }; const caseInfoData2: CaseInfoType = { @@ -18,7 +18,7 @@ const caseInfoData2: CaseInfoType = { leadDefendantFirstNames: 'John', leadDefendantSurname: 'Doe', numberOfDefendants: 1, - unitName: 'Unit name 2' + unitName: 'Unit name 2', }; describe('stores > useCaseInfoStore', () => { diff --git a/materials_ui/src/__tests__/stores/useMaterialTags.test.ts b/materials_ui/src/__tests__/stores/useMaterialTags.test.ts index f49d8993..8c2b8c76 100644 --- a/materials_ui/src/__tests__/stores/useMaterialTags.test.ts +++ b/materials_ui/src/__tests__/stores/useMaterialTags.test.ts @@ -10,7 +10,7 @@ describe('useMaterialTags Store', () => { it('should set new tags correctly', () => { const newTags = [ { materialId: 1, tagName: 'tag1' }, - { materialId: 2, tagName: 'tag2' } + { materialId: 2, tagName: 'tag2' }, ]; useMaterialTags.getState().setTags(newTags); @@ -22,14 +22,14 @@ describe('useMaterialTags Store', () => { it('should update existing tags when the same materialId is added with new tagName', () => { const initialTags = [ { materialId: 1, tagName: 'tag1' }, - { materialId: 2, tagName: 'tag2' } + { materialId: 2, tagName: 'tag2' }, ]; useMaterialTags.getState().setTags(initialTags); const updatedTags = [ { materialId: 1, tagName: 'updatedTag1' }, - { materialId: 3, tagName: 'tag3' } + { materialId: 3, tagName: 'tag3' }, ]; useMaterialTags.getState().setTags(updatedTags); @@ -38,21 +38,21 @@ describe('useMaterialTags Store', () => { expect(store.materialTags).toEqual([ { materialId: 1, tagName: 'updatedTag1' }, { materialId: 2, tagName: 'tag2' }, - { materialId: 3, tagName: 'tag3' } + { materialId: 3, tagName: 'tag3' }, ]); }); it('should add new tags and update existing ones with the same materialId', () => { const newTags = [ { materialId: 1, tagName: 'tag1' }, - { materialId: 2, tagName: 'tag2' } + { materialId: 2, tagName: 'tag2' }, ]; useMaterialTags.getState().setTags(newTags); const duplicateTags = [ { materialId: 1, tagName: 'newTag1' }, - { materialId: 3, tagName: 'newTag3' } + { materialId: 3, tagName: 'newTag3' }, ]; useMaterialTags.getState().setTags(duplicateTags); @@ -61,14 +61,14 @@ describe('useMaterialTags Store', () => { expect(store.materialTags).toEqual([ { materialId: 1, tagName: 'newTag1' }, { materialId: 2, tagName: 'tag2' }, - { materialId: 3, tagName: 'newTag3' } + { materialId: 3, tagName: 'newTag3' }, ]); }); it('should clear all tags when clearTags is called with no materialIdsToRemove', () => { const newTags = [ { materialId: 1, tagName: 'tag1' }, - { materialId: 2, tagName: 'tag2' } + { materialId: 2, tagName: 'tag2' }, ]; useMaterialTags.getState().setTags(newTags); @@ -82,7 +82,7 @@ describe('useMaterialTags Store', () => { const newTags = [ { materialId: 1, tagName: 'tag1' }, { materialId: 2, tagName: 'tag2' }, - { materialId: 3, tagName: 'tag3' } + { materialId: 3, tagName: 'tag3' }, ]; useMaterialTags.getState().setTags(newTags); @@ -96,7 +96,7 @@ describe('useMaterialTags Store', () => { it('should not modify tags when clearTags is called with an empty array', () => { const newTags = [ { materialId: 1, tagName: 'tag1' }, - { materialId: 2, tagName: 'tag2' } + { materialId: 2, tagName: 'tag2' }, ]; useMaterialTags.getState().setTags(newTags); diff --git a/materials_ui/src/__tests__/stores/useSelectedItems.test.ts b/materials_ui/src/__tests__/stores/useSelectedItems.test.ts index 3d9620fd..5c48edb0 100644 --- a/materials_ui/src/__tests__/stores/useSelectedItems.test.ts +++ b/materials_ui/src/__tests__/stores/useSelectedItems.test.ts @@ -28,15 +28,13 @@ function getMaterial(override?: Partial): CaseMaterialsType { item: '', existingproducerOrWitnessId: 0, isReclassifiable: false, - ...override + ...override, }; } describe('stores > useSelectedItems', () => { beforeEach(() => { - useSelectedItemsStore.setState({ - items: { communications: [], materials: [] } - }); + useSelectedItemsStore.setState({ items: { communications: [], materials: [] } }); }); it('should initialise as expected', () => { @@ -50,16 +48,12 @@ describe('stores > useSelectedItems', () => { useSelectedItemsStore.getState().addItems([getMaterial()], 'materials'); expect(useSelectedItemsStore.getState().items.materials).toHaveLength(1); - useSelectedItemsStore - .getState() - .addItems([getMaterial({ id: 2 })], 'materials'); + useSelectedItemsStore.getState().addItems([getMaterial({ id: 2 })], 'materials'); expect(useSelectedItemsStore.getState().items.materials).toHaveLength(2); }); it('should add multiple material items to state (addItems())', () => { - useSelectedItemsStore - .getState() - .addItems([getMaterial(), getMaterial({ id: 2 })], 'materials'); + useSelectedItemsStore.getState().addItems([getMaterial(), getMaterial({ id: 2 })], 'materials'); expect(useSelectedItemsStore.getState().items.materials).toHaveLength(2); }); @@ -67,21 +61,15 @@ describe('stores > useSelectedItems', () => { useSelectedItemsStore.getState().addItems([getMaterial()], 'materials'); expect(useSelectedItemsStore.getState().items.materials).toHaveLength(1); - useSelectedItemsStore - .getState() - .addItems([getMaterial(), getMaterial({ id: 2 })], 'materials'); + useSelectedItemsStore.getState().addItems([getMaterial(), getMaterial({ id: 2 })], 'materials'); expect(useSelectedItemsStore.getState().items.materials).toHaveLength(2); }); it('should remove a single material item from state (removeItems())', () => { - useSelectedItemsStore - .getState() - .addItems([getMaterial(), getMaterial({ id: 2 })], 'materials'); + useSelectedItemsStore.getState().addItems([getMaterial(), getMaterial({ id: 2 })], 'materials'); expect(useSelectedItemsStore.getState().items.materials).toHaveLength(2); - useSelectedItemsStore - .getState() - .removeItems([getMaterial({ id: 2 })], 'materials'); + useSelectedItemsStore.getState().removeItems([getMaterial({ id: 2 })], 'materials'); expect(useSelectedItemsStore.getState().items.materials).toHaveLength(1); }); @@ -89,66 +77,40 @@ describe('stores > useSelectedItems', () => { useSelectedItemsStore .getState() .addItems( - [ - getMaterial(), - getMaterial({ id: 2 }), - getMaterial({ id: 3 }), - getMaterial({ id: 4 }) - ], - 'materials' + [getMaterial(), getMaterial({ id: 2 }), getMaterial({ id: 3 }), getMaterial({ id: 4 })], + 'materials', ); expect(useSelectedItemsStore.getState().items.materials).toHaveLength(4); useSelectedItemsStore .getState() - .removeItems( - [getMaterial({ id: 2 }), getMaterial({ id: 4 })], - 'materials' - ); + .removeItems([getMaterial({ id: 2 }), getMaterial({ id: 4 })], 'materials'); expect(useSelectedItemsStore.getState().items.materials).toHaveLength(2); }); it('should clear items from one type (clear(type))', () => { + useSelectedItemsStore.getState().addItems([getMaterial(), getMaterial({ id: 2 })], 'materials'); useSelectedItemsStore .getState() - .addItems([getMaterial(), getMaterial({ id: 2 })], 'materials'); - useSelectedItemsStore - .getState() - .addItems( - [getMaterial({ id: 3 }), getMaterial({ id: 4 })], - 'communications' - ); + .addItems([getMaterial({ id: 3 }), getMaterial({ id: 4 })], 'communications'); expect(useSelectedItemsStore.getState().items.materials).toHaveLength(2); - expect(useSelectedItemsStore.getState().items.communications).toHaveLength( - 2 - ); + expect(useSelectedItemsStore.getState().items.communications).toHaveLength(2); useSelectedItemsStore.getState().clear('communications'); - expect(useSelectedItemsStore.getState().items.communications).toHaveLength( - 0 - ); + expect(useSelectedItemsStore.getState().items.communications).toHaveLength(0); expect(useSelectedItemsStore.getState().items.materials).toHaveLength(2); }); it('should clear all items (clear())', () => { + useSelectedItemsStore.getState().addItems([getMaterial(), getMaterial({ id: 2 })], 'materials'); useSelectedItemsStore .getState() - .addItems([getMaterial(), getMaterial({ id: 2 })], 'materials'); - useSelectedItemsStore - .getState() - .addItems( - [getMaterial({ id: 3 }), getMaterial({ id: 4 })], - 'communications' - ); + .addItems([getMaterial({ id: 3 }), getMaterial({ id: 4 })], 'communications'); expect(useSelectedItemsStore.getState().items.materials).toHaveLength(2); - expect(useSelectedItemsStore.getState().items.communications).toHaveLength( - 2 - ); + expect(useSelectedItemsStore.getState().items.communications).toHaveLength(2); useSelectedItemsStore.getState().clear(); - expect(useSelectedItemsStore.getState().items.communications).toHaveLength( - 0 - ); + expect(useSelectedItemsStore.getState().items.communications).toHaveLength(0); expect(useSelectedItemsStore.getState().items.materials).toHaveLength(0); }); }); diff --git a/materials_ui/src/app.tsx b/materials_ui/src/app.tsx index 1997d9ec..363eeff1 100644 --- a/materials_ui/src/app.tsx +++ b/materials_ui/src/app.tsx @@ -3,6 +3,13 @@ import { useEffect } from 'react'; import { RouteChangeListener } from './components'; import { loginRequest } from './msalInstance'; import { Routes } from './routes'; +import { + setTelemetryAuthenticatedUser, + setTelemetryUserRole, + trackAppOpened, +} from './telemetry/appInsights'; + +let appOpenedTracked = false; export const App = () => { const { instance, accounts } = useMsal(); @@ -14,6 +21,23 @@ export const App = () => { }, [instance, accounts]); const account = instance.getActiveAccount() || accounts[0]; + const role = (account?.idTokenClaims?.roles as string[] | undefined)?.join(','); + + useEffect(() => { + if (role) setTelemetryUserRole(role); + }, [role]); + + const accountId = account?.localAccountId; + + useEffect(() => { + if (!accountId) return; + // Stamp the signed-in user first so the AppOpened event carries it. + setTelemetryAuthenticatedUser(accountId); + if (!appOpenedTracked) { + appOpenedTracked = true; + trackAppOpened(); + } + }, [accountId]); if (!account) { return

Redirecting to login...

; diff --git a/materials_ui/src/caseWorkApp/assetsCWA/svgs/DownArrowIcon.tsx b/materials_ui/src/caseWorkApp/assetsCWA/svgs/DownArrowIcon.tsx index 7780a698..86e770bf 100644 --- a/materials_ui/src/caseWorkApp/assetsCWA/svgs/DownArrowIcon.tsx +++ b/materials_ui/src/caseWorkApp/assetsCWA/svgs/DownArrowIcon.tsx @@ -1,8 +1,4 @@ -export const DownArrowIcon = (p: { - color: string; - rotateDegrees?: number; - scale?: number; -}) => { +export const DownArrowIcon = (p: { color: string; rotateDegrees?: number; scale?: number }) => { const rotateDegrees = p.rotateDegrees ?? 0; const scale = p.scale ?? 1; return ( diff --git a/materials_ui/src/caseWorkApp/components/LinkButton/LinkButton.module.scss b/materials_ui/src/caseWorkApp/components/LinkButton/LinkButton.module.scss index 818feffa..610ad082 100644 --- a/materials_ui/src/caseWorkApp/components/LinkButton/LinkButton.module.scss +++ b/materials_ui/src/caseWorkApp/components/LinkButton/LinkButton.module.scss @@ -7,7 +7,7 @@ font-size: 1.187rem; width: fit-content; color: #1d70b8; - font-family: "NewTransport", "Arial", sans-serif; + font-family: 'NewTransport', 'Arial', sans-serif; &:hover:not(:disabled):not(:focus) { color: #fff; diff --git a/materials_ui/src/caseWorkApp/components/LinkButton/LinkButton.tsx b/materials_ui/src/caseWorkApp/components/LinkButton/LinkButton.tsx index ca81a705..24ba8d33 100644 --- a/materials_ui/src/caseWorkApp/components/LinkButton/LinkButton.tsx +++ b/materials_ui/src/caseWorkApp/components/LinkButton/LinkButton.tsx @@ -26,9 +26,9 @@ export const LinkButton = forwardRef( ariaLabel, ariaExpanded, disabled = false, - type + type, }, - ref + ref, ) => { const resolvedClassName = `${classes.linkButton} ${className}`; return ( @@ -47,5 +47,5 @@ export const LinkButton = forwardRef( {children} ); - } + }, ); diff --git a/materials_ui/src/caseWorkApp/components/button/index.tsx b/materials_ui/src/caseWorkApp/components/button/index.tsx index dc6e8e18..f590149b 100644 --- a/materials_ui/src/caseWorkApp/components/button/index.tsx +++ b/materials_ui/src/caseWorkApp/components/button/index.tsx @@ -14,29 +14,19 @@ const buttonVariantMap: { [k in TButtonVariant]: string } = { inverse: 'govuk-button--inverse', default: 'govuk-button--secondary', secondary: 'govuk-button--secondary', - red: 'govuk-button--red' // custom class to maintain consistency, not required + red: 'govuk-button--red', // custom class to maintain consistency, not required }; const buttonSizeStyleMap: { [k in TButtonSize]: CSSProperties } = { s: { fontSize: '0.875rem' }, - m: {} + m: {}, }; const buttonVariantStyleMap: { [k in TButtonVariant]?: CSSProperties } = { - red: { backgroundColor: '#d4351c', color: 'white' } + red: { backgroundColor: '#d4351c', color: 'white' }, }; export const Button = forwardRef( - ( - { - variant = 'default', - size = 'm', - className, - style, - children, - ...restProps - }, - ref - ) => { + ({ variant = 'default', size = 'm', className, style, children, ...restProps }, ref) => { const variantClass = buttonVariantMap[variant]; const variantStyle = buttonVariantStyleMap[variant]; const sizeStyle = buttonSizeStyleMap[size]; @@ -52,5 +42,5 @@ export const Button = forwardRef( ); - } + }, ); diff --git a/materials_ui/src/caseWorkApp/components/dropDownButton/DropdownButton.module.scss b/materials_ui/src/caseWorkApp/components/dropDownButton/DropdownButton.module.scss index 1fa57966..17b30241 100644 --- a/materials_ui/src/caseWorkApp/components/dropDownButton/DropdownButton.module.scss +++ b/materials_ui/src/caseWorkApp/components/dropDownButton/DropdownButton.module.scss @@ -58,5 +58,3 @@ color: #b1b4b6; } } - - diff --git a/materials_ui/src/caseWorkApp/components/dropDownButton/DropdownButton.tsx b/materials_ui/src/caseWorkApp/components/dropDownButton/DropdownButton.tsx index dc3b86e7..03dc96b9 100644 --- a/materials_ui/src/caseWorkApp/components/dropDownButton/DropdownButton.tsx +++ b/materials_ui/src/caseWorkApp/components/dropDownButton/DropdownButton.tsx @@ -8,23 +8,12 @@ import classes from './DropdownButton.module.scss'; type TButtonProps = React.ComponentPropsWithoutRef; -export const DropdownListItem = ( - initProps: TButtonProps & { borderBottom: boolean } -) => { +export const DropdownListItem = (initProps: TButtonProps & { borderBottom: boolean }) => { const { borderBottom, ...props } = initProps; return ( -
+
@@ -81,36 +70,18 @@ export const DropdownButton2 = (p: { return ( - {p.isOpen && (
-
+
{p.children}
@@ -144,18 +115,13 @@ export const DropdownButton: React.FC = ({ dataTestId = 'dropdown-btn', ariaLabel = 'dropdown', disabled = false, - iconScale = 1 + iconScale = 1, }) => { const dropDownBtnRef = useRef(null); const panelRef = useRef(null); const [buttonOpen, setButtonOpen] = useState(false); - useGlobalDropdownClose( - dropDownBtnRef, - panelRef, - setButtonOpen, - '#dropdown-panel' - ); + useGlobalDropdownClose(dropDownBtnRef, panelRef, setButtonOpen, '#dropdown-panel'); const handleBtnClick = (id: string) => { setButtonOpen(false); @@ -178,18 +144,11 @@ export const DropdownButton: React.FC = ({ >
{name && ( - + {name} )} - +
diff --git a/materials_ui/src/caseWorkApp/components/tabs/TabButtons.tsx b/materials_ui/src/caseWorkApp/components/tabs/TabButtons.tsx index 84d5cf6f..418f606e 100644 --- a/materials_ui/src/caseWorkApp/components/tabs/TabButtons.tsx +++ b/materials_ui/src/caseWorkApp/components/tabs/TabButtons.tsx @@ -16,28 +16,20 @@ const TabButtons: React.FC = ({ items, activeTabIndex, handleTabSelection, - handleCloseTab + handleCloseTab, }) => { const activeTabRef = useRef(null); useEffect(() => { activeTabRef.current?.focus(); - activeTabRef.current?.parentElement?.scrollIntoView({ - behavior: 'smooth', - block: 'nearest' - }); + activeTabRef.current?.parentElement?.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); }, [activeTabIndex, items.length]); type ArrowKeyCodes = 'ArrowLeft' | 'ArrowRight'; - const ARROW_KEY_SHIFTS: Record = { - ArrowLeft: -1, - ArrowRight: 1 - }; + const ARROW_KEY_SHIFTS: Record = { ArrowLeft: -1, ArrowRight: 1 }; - const handleKeyPressOnTab: React.KeyboardEventHandler = ( - ev - ) => { + const handleKeyPressOnTab: React.KeyboardEventHandler = (ev) => { if (ev.code in ARROW_KEY_SHIFTS) { const thisShift = ARROW_KEY_SHIFTS[ev.code as ArrowKeyCodes]; // -1, 1, or undefined moveToNextOrPreviousTab(thisShift); @@ -50,7 +42,7 @@ const TabButtons: React.FC = ({ document.getElementById('tabs-dropdown')?.click(); setTimeout(() => { const firstItem = document.querySelector( - '#dropdown-panel button:not(:disabled)' + '#dropdown-panel button:not(:disabled)', ); firstItem?.focus(); }, 0); @@ -76,10 +68,7 @@ const TabButtons: React.FC = ({ }; const tabDropdownItems = useMemo(() => { - return items.map((item) => ({ - ...item, - disabled: item.id === items[activeTabIndex]?.id - })); + return items.map((item) => ({ ...item, disabled: item.id === items[activeTabIndex]?.id })); }, [items, activeTabIndex]); if (!items.length) { @@ -116,9 +105,7 @@ const TabButtons: React.FC = ({
-
+
    {items.map((item, index) => { const { id, label, ariaLabel } = item; @@ -126,9 +113,7 @@ const TabButtons: React.FC = ({ return (
  • = ({ > +
+ + } + /> + + ); +}; diff --git a/materials_ui/src/caseWorkApp/pages/ReviewAndRedactPage/Modal.tsx b/materials_ui/src/caseWorkApp/pages/ReviewAndRedactPage/Modal.tsx index d934f826..d6e5f56d 100644 --- a/materials_ui/src/caseWorkApp/pages/ReviewAndRedactPage/Modal.tsx +++ b/materials_ui/src/caseWorkApp/pages/ReviewAndRedactPage/Modal.tsx @@ -6,11 +6,7 @@ export type ModalProps = { onEscPress: () => void; }; -export const Modal = ({ - children, - onBackgroundClick, - onEscPress -}: ModalProps) => { +export const Modal = ({ children, onBackgroundClick, onEscPress }: ModalProps) => { const popupRef = useRef(null); useEffect(() => { @@ -41,7 +37,7 @@ export const Modal = ({ zIndex: 999, display: 'flex', justifyContent: 'center', - alignItems: 'center' + alignItems: 'center', }} onClick={onBackgroundClick} > @@ -53,7 +49,7 @@ export const Modal = ({ boxShadow: '0 0 5px 5px #0003', zIndex: 1000, filter: 'drop-shadow(0 1px 2.5px #000)', - overflow: 'hidden' + overflow: 'hidden', }} onClick={(e) => e.stopPropagation()} > diff --git a/materials_ui/src/caseWorkApp/pages/ReviewAndRedactPage/ReviewAndRedactPage.tsx b/materials_ui/src/caseWorkApp/pages/ReviewAndRedactPage/ReviewAndRedactPage.tsx index a13b814c..603b31cf 100644 --- a/materials_ui/src/caseWorkApp/pages/ReviewAndRedactPage/ReviewAndRedactPage.tsx +++ b/materials_ui/src/caseWorkApp/pages/ReviewAndRedactPage/ReviewAndRedactPage.tsx @@ -5,7 +5,7 @@ import { Layout, LoadingSpinner, RenameDrawer, - TwoCol + TwoCol, } from '../../../components'; import { useCaseInfoStore } from '../../../hooks'; import { navigateToViewDocumentPageInNewTab } from '../../../hooks/ui/navigateToViewDocumentPageInNewTab'; @@ -15,22 +15,23 @@ import { TDocument } from '../../../materials_components/DocumentSelectAccordion import { clearOpenDocumentTabsFromLocalStorage, safeGetOpenDocumentTabsFromLocalStorage, - safeSetOpenDocumentTabsFromLocalStorage + safeSetOpenDocumentTabsFromLocalStorage, } from '../../../materials_components/DocumentSelectAccordion/utils/OpenDocumentTabsLocalStorageUtils'; import { DocSearchContext, - DocumentTabPanel + DocumentTabPanel, } from '../../../materials_components/DocumentTabPanel/DocumentTabPanel'; import { DocumentActionsDropdown } from '../../../materials_components/documenViewportArea/DocumentActionsDropdown'; import { TRedaction } from '../../../materials_components/PdfRedactor/utils/coordUtils'; import { isRedactionEnabledMode, - TMode + TMode, } from '../../../materials_components/PdfRedactor/utils/modeUtils'; import { convertMatchesToSearchHighlights } from '../../../materials_components/PdfRedactor/utils/searchHighlightUtils'; import { useTrigger } from '../../../materials_components/PdfRedactor/utils/useTriggger'; import { RedactionLogModal } from '../../../materials_components/RedactionLog/RedactionLogModal'; import type { SearchTermResultType } from '../../../schemas/documents'; +import { trackAction, trackMetric } from '../../../telemetry/appInsights'; import { Tabs } from '../../components/tabs'; import { getLookups, useAxiosInstances } from '../../components/utils/getData'; import { useSwitchContentArea } from '../../hooks/useSwitchContentArea'; @@ -51,7 +52,7 @@ export const ReviewAndRedactPage = () => { docType: docTypeParam, materialId: materialIdParam, searchTerm: searchTermParam, - searchMatches: searchMatchesParam + searchMatches: searchMatchesParam, } = (locationState || {}) as { docType?: string; materialId?: string; @@ -68,8 +69,9 @@ export const ReviewAndRedactPage = () => { (TDocument & { materialId?: number }) | null >(null); - const [redactionsIndexedOnParentId, setRedactionsIndexedOnParentId] = - useState<{ [k: string]: TRedaction[] }>({}); + const [redactionsIndexedOnParentId, setRedactionsIndexedOnParentId] = useState<{ + [k: string]: TRedaction[]; + }>({}); const [searchContextByParentId, setSearchContextByParentId] = useState< Record @@ -85,18 +87,13 @@ export const ReviewAndRedactPage = () => { const [activeParentId, setActiveParentId] = useState(''); const [newVersionParentId, setNewVersionParentId] = useState(''); - const [modeByParentId, setModeByParentId] = useState>( - {} - ); - const [numOfPagesByParentId, setNumOfPagesByParentId] = useState< - Record - >({}); + const [modeByParentId, setModeByParentId] = useState>({}); + const [numOfPagesByParentId, setNumOfPagesByParentId] = useState>({}); const [searchModalOpen, setSearchModalOpen] = useState(false); const [unsavedModal, setUnsavedModal] = useState(null); - const [pendingUnsavedAction, setPendingUnsavedAction] = - useState(); + const [pendingUnsavedAction, setPendingUnsavedAction] = useState(); const [documents, setDocuments] = useState(); const documentsRef = useRef(undefined); @@ -133,7 +130,7 @@ export const ReviewAndRedactPage = () => { safeSetOpenDocumentTabsFromLocalStorage({ caseId, openParentIds: openParentIds, - activeParentId: activeParentId + activeParentId: activeParentId, }); } }, [caseId, openParentIds, activeParentId]); @@ -144,18 +141,14 @@ export const ReviewAndRedactPage = () => { setOpenParentIds((openParentIds) => openParentIds.includes(`${materialIdParam}`) ? openParentIds - : [...openParentIds, `${materialIdParam}`] + : [...openParentIds, `${materialIdParam}`], ); if (searchTermParam && searchMatchesParam) { const highlights = convertMatchesToSearchHighlights(searchMatchesParam); setSearchContextByParentId((prev) => ({ ...prev, - [materialIdParam]: { - searchTerm: searchTermParam, - highlights, - focusedIndex: 0 - } + [materialIdParam]: { searchTerm: searchTermParam, highlights, focusedIndex: 0 }, })); } @@ -184,17 +177,13 @@ export const ReviewAndRedactPage = () => { if (docTypeParam && documents && documents.length > 0) { const filteredDocs = documents.filter( (doc) => - doc.cmsDocType.documentType === docTypeParam && - !openParentIds.includes(doc.parentId) + doc.cmsDocType.documentType === docTypeParam && !openParentIds.includes(doc.parentId), ); if (filteredDocs.length) { const newActiveParentId = filteredDocs[0]?.parentId; if (newActiveParentId) setActiveParentId(newActiveParentId); - setOpenParentIds((prev) => [ - ...prev, - ...filteredDocs.map((doc) => doc.parentId) - ]); + setOpenParentIds((prev) => [...prev, ...filteredDocs.map((doc) => doc.parentId)]); } } }, [docTypeParam, documents]); @@ -223,10 +212,7 @@ export const ReviewAndRedactPage = () => { mode={modeByParentId[doc.parentId] ?? 'disabled'} onModeChange={(newMode) => handleModeChange(doc.parentId, newMode)} onRedactionsChange={(redactions) => { - setRedactionsIndexedOnParentId((prev) => ({ - ...prev, - [doc.parentId]: redactions - })); + setRedactionsIndexedOnParentId((prev) => ({ ...prev, [doc.parentId]: redactions })); }} onModification={(document) => { setNewVersionParentId(document.parentId); @@ -234,20 +220,15 @@ export const ReviewAndRedactPage = () => { }} initRedactions={redactionsIndexedOnParentId[doc.parentId]} onNumOfPagesChange={(numOfPages) => - setNumOfPagesByParentId((prev) => ({ - ...prev, - [doc.parentId]: numOfPages - })) + setNumOfPagesByParentId((prev) => ({ ...prev, [doc.parentId]: numOfPages })) } searchContext={searchContextByParentId[doc.parentId]} - onFocusedSearchIndexChange={(index) => - setFocusedSearchIndex(doc.parentId, index) - } + onFocusedSearchIndexChange={(index) => setFocusedSearchIndex(doc.parentId, index)} onBackToSearchResults={() => setSearchModalOpen(true)} checkInDocumentTriggerData={checkInDocumentTrigger.data} /> - ) - } + ), + }, })); const performCloseTab = (documentId: string | undefined) => { @@ -258,13 +239,12 @@ export const ReviewAndRedactPage = () => { caseId, urn, parentId: document.parentId, - childId: document.childId + childId: document.childId, }); } if (documentId && documentId === activeParentId) { const index = openParentIds.indexOf(documentId); - const nextDocumentId = - openParentIds[index + 1] ?? openParentIds[index - 1] ?? ''; + const nextDocumentId = openParentIds[index + 1] ?? openParentIds[index - 1] ?? ''; setActiveParentId(nextDocumentId); } setOpenParentIds((prev) => prev.filter((id) => id !== documentId)); @@ -282,7 +262,7 @@ export const ReviewAndRedactPage = () => { caseId, urn, parentId: document.parentId, - childId: document.childId + childId: document.childId, }); }); } @@ -294,9 +274,8 @@ export const ReviewAndRedactPage = () => { setNumOfPagesByParentId({}); const sidebarFocusTarget = - document.querySelector( - '#side-panel .govuk-accordion__section-button' - ) ?? document.querySelector('#side-panel'); + document.querySelector('#side-panel .govuk-accordion__section-button') ?? + document.querySelector('#side-panel'); sidebarFocusTarget?.focus(); }; @@ -318,15 +297,8 @@ export const ReviewAndRedactPage = () => { }; const requestActiveTabChange = (nextParentId: string) => { - if ( - nextParentId !== activeParentId && - hasUnsavedRedactions(activeParentId) - ) { - setPendingUnsavedAction({ - kind: 'switchTab', - parentId: activeParentId, - nextParentId - }); + if (nextParentId !== activeParentId && hasUnsavedRedactions(activeParentId)) { + setPendingUnsavedAction({ kind: 'switchTab', parentId: activeParentId, nextParentId }); return; } setActiveParentId(nextParentId); @@ -334,8 +306,7 @@ export const ReviewAndRedactPage = () => { const handleModeChange = (parentId: string, newMode: TMode) => { const isStoppingRedaction = - isRedactionEnabledMode(modeByParentId[parentId] ?? 'disabled') && - newMode === 'disabled'; + isRedactionEnabledMode(modeByParentId[parentId] ?? 'disabled') && newMode === 'disabled'; if (isStoppingRedaction && hasUnsavedRedactions(parentId)) { setPendingUnsavedAction({ kind: 'stopRedacting', parentId }); return; @@ -355,29 +326,26 @@ export const ReviewAndRedactPage = () => { setActiveParentId(action.nextParentId); break; case 'stopRedacting': - setModeByParentId((prev) => ({ - ...prev, - [action.parentId]: 'disabled' - })); + setModeByParentId((prev) => ({ ...prev, [action.parentId]: 'disabled' })); break; } }; const activeTabId = activeParentId || openParentIds[0] || ''; - const activeDocument = openDocuments.find( - (doc) => doc.parentId === activeTabId - ); + useEffect(() => { + if (!activeTabId) return; + const start = Date.now(); + return () => trackMetric('TabViewTime', Date.now() - start); + }, [activeTabId]); - const openUnsavedRedactions: { [k: string]: TRedaction[] } = - Object.fromEntries( - openParentIds - .filter(hasUnsavedRedactions) - .map((parentId) => [ - parentId, - redactionsIndexedOnParentId[parentId] ?? [] - ]) - ); + const activeDocument = openDocuments.find((doc) => doc.parentId === activeTabId); + + const openUnsavedRedactions: { [k: string]: TRedaction[] } = Object.fromEntries( + openParentIds + .filter(hasUnsavedRedactions) + .map((parentId) => [parentId, redactionsIndexedOnParentId[parentId] ?? []]), + ); useEffect(() => { if (showRedactionLogModal) { @@ -392,7 +360,7 @@ export const ReviewAndRedactPage = () => { title="Review and Redact" shouldBlockNavigationCheck={(tab) => { const shouldBlock = Object.values(redactionsIndexedOnParentId).some( - (redacts) => redacts.length > 0 + (redacts) => redacts.length > 0, ); if (!shouldBlock) return false; @@ -400,18 +368,13 @@ export const ReviewAndRedactPage = () => { return true; }} > - + {documents === null &&
Error...
} {unsavedModal && ( setUnsavedModal(null)} onIgnoreClick={() => { @@ -427,9 +390,7 @@ export const ReviewAndRedactPage = () => { )} {pendingUnsavedAction && ( setPendingUnsavedAction(undefined)} onIgnoreClick={proceedWithPendingAction} /> @@ -498,17 +459,12 @@ export const ReviewAndRedactPage = () => { documentActions={ - handleModeChange(activeTabId, newMode) - } + onModeChange={(newMode) => handleModeChange(activeTabId, newMode)} onRedactionLogClick={() => setShowRedactionLogModal(true)} onViewInNewWindowClick={() => { if (!urn || !caseId) return; - navigateToViewDocumentPageInNewTab({ - urn, - caseId, - materialId: activeTabId - }); + trackAction('OpenedInNewWindow', { materialId: activeTabId }); + navigateToViewDocumentPageInNewTab({ urn, caseId, materialId: activeTabId }); }} numOfDocumentPages={numOfPagesByParentId[activeTabId] ?? 0} /> diff --git a/materials_ui/src/caseWorkApp/pages/ReviewAndRedactPage/UnsavedRedactionsModal.tsx b/materials_ui/src/caseWorkApp/pages/ReviewAndRedactPage/UnsavedRedactionsModal.tsx index 2a2f56d3..2a386b02 100644 --- a/materials_ui/src/caseWorkApp/pages/ReviewAndRedactPage/UnsavedRedactionsModal.tsx +++ b/materials_ui/src/caseWorkApp/pages/ReviewAndRedactPage/UnsavedRedactionsModal.tsx @@ -17,18 +17,15 @@ export const UnsavedRedactionsModal = ({ documents, onReturnClick, onIgnoreClick, - onDocumentClick + onDocumentClick, }: UnsavedRedactionsModalProps) => { - const documentIdsWithRedactions = Object.keys( - redactionsIndexedOnDocumentId - ).filter( + const documentIdsWithRedactions = Object.keys(redactionsIndexedOnDocumentId).filter( (docId) => - redactionsIndexedOnDocumentId[docId] && - redactionsIndexedOnDocumentId[docId].length > 0 + redactionsIndexedOnDocumentId[docId] && redactionsIndexedOnDocumentId[docId].length > 0, ); const documentsWithRedactions = documents.filter((doc) => - documentIdsWithRedactions.includes(doc.parentId) + documentIdsWithRedactions.includes(doc.parentId), ); const isPluralDocuments = documentsWithRedactions.length !== 1; @@ -41,9 +38,7 @@ export const UnsavedRedactionsModal = ({ contentHeading={`You have ${documentsWithRedactions.length} document${isPluralDocuments ? 's' : ''} with unsaved redactions `} contentBody={ <> -
+
{documentsWithRedactions.map((doc) => (
-
- Return to the case to save your redactions in these documents -
+
Return to the case to save your redactions in these documents
If you select Ignore your redactions will not be applied.

@@ -62,12 +53,7 @@ export const Accordion = ({ items = [], plain = false }: AccordionProps) => { data-testid="accordion" > {items.map((item, index) => ( - + ))}
); diff --git a/materials_ui/src/components/Banner/Banner.scss b/materials_ui/src/components/Banner/Banner.scss index fe02bc0d..a46d8f9d 100644 --- a/materials_ui/src/components/Banner/Banner.scss +++ b/materials_ui/src/components/Banner/Banner.scss @@ -12,4 +12,4 @@ max-width: 66.666%; } } -} \ No newline at end of file +} diff --git a/materials_ui/src/components/Banner/Banner.tsx b/materials_ui/src/components/Banner/Banner.tsx index 24eb52c4..5e4dd963 100644 --- a/materials_ui/src/components/Banner/Banner.tsx +++ b/materials_ui/src/components/Banner/Banner.tsx @@ -4,11 +4,7 @@ import './Banner.scss'; type Props = BannerType; -export const Banner = ({ - type = 'success', - header, - content -}: PropsWithChildren) => { +export const Banner = ({ type = 'success', header, content }: PropsWithChildren) => { const bannerRef = useRef(null); useEffect(() => { @@ -39,20 +35,20 @@ export const Banner = ({ } }; + const title = `${getBannerTitle()}: ${header}`; + const announcement = content ? `${title}. ${content}` : title; + return (
-

+

{getBannerTitle()}

diff --git a/materials_ui/src/components/Button/AutoReclassifyButton.tsx b/materials_ui/src/components/Button/AutoReclassifyButton.tsx index 7d408447..4f44c3ab 100644 --- a/materials_ui/src/components/Button/AutoReclassifyButton.tsx +++ b/materials_ui/src/components/Button/AutoReclassifyButton.tsx @@ -2,13 +2,12 @@ import { useState } from 'react'; import { useAutoReclassify, useBanner, useCaseMaterials } from '../../hooks'; import { useMaterialTags } from '../../stores'; +import { trackAction } from '../../telemetry/appInsights'; export const AutoReclassifyButton = () => { const [errorCount, setErrorCount] = useState(0); const { resetBanner, setBanner } = useBanner(); - const { mutate: refreshMaterials } = useCaseMaterials({ - dataType: 'materials' - }); + const { mutate: refreshMaterials } = useCaseMaterials({ dataType: 'materials' }); const { setTags } = useMaterialTags(); const { mutate: submitAutoReclassifyRequest } = useAutoReclassify({ @@ -19,7 +18,7 @@ export const AutoReclassifyButton = () => { setBanner({ type: 'important', header: 'Important', - content: 'No materials were identified for reclassification.' + content: 'No materials were identified for reclassification.', }); } @@ -28,15 +27,14 @@ export const AutoReclassifyButton = () => { setBanner({ type: 'error', header: 'An error occurred', - content: - 'There was a problem reclassifying materials. Please try again.' + content: 'There was a problem reclassifying materials. Please try again.', }); } else { setBanner({ type: 'error', header: 'An error occurred', content: - 'Materials cannot be reclassified automatically. You can still reclassify materials manually.' + 'Materials cannot be reclassified automatically. You can still reclassify materials manually.', }); } } @@ -45,23 +43,24 @@ export const AutoReclassifyButton = () => { }, onSuccess: async (data) => { const totalMaterialsProcessed = data.reclassifiedMaterials.length || 1; + trackAction('UpdatedAutomatically', { count: totalMaterialsProcessed }); setTags( data?.reclassifiedMaterials?.map((material) => ({ materialId: material?.materialId, - tagName: 'Reclassified' - })) + tagName: 'Reclassified', + })), ); setErrorCount(0); setBanner({ type: 'success', header: 'Reclassification successful', - content: `${totalMaterialsProcessed} Unused Material${totalMaterialsProcessed === 1 ? '' : 's'} reclassified successfully.` + content: `${totalMaterialsProcessed} Unused Material${totalMaterialsProcessed === 1 ? '' : 's'} reclassified successfully.`, }); await refreshMaterials(); - } + }, }); const handleClick = async () => { diff --git a/materials_ui/src/components/ButtonMenu/ButtonMenu.tsx b/materials_ui/src/components/ButtonMenu/ButtonMenu.tsx index 49e5a865..91084090 100644 --- a/materials_ui/src/components/ButtonMenu/ButtonMenu.tsx +++ b/materials_ui/src/components/ButtonMenu/ButtonMenu.tsx @@ -1,29 +1,18 @@ import { KeyboardEvent, useEffect, useRef, useState } from 'react'; import './_button-menu.scss'; -type MenuItem = { - label: string; - onClick: () => void; - className?: string; - hide?: boolean; -}; +type MenuItem = { label: string; onClick: () => void; className?: string; hide?: boolean }; type Props = { menuTitle: string; menuItems: MenuItem[]; isDisabled?: boolean }; -export function ButtonMenuComponent({ - menuTitle, - menuItems, - isDisabled = false -}: Props) { +export function ButtonMenuComponent({ menuTitle, menuItems, isDisabled = false }: Props) { const [openMenu, setOpenMenu] = useState(false); const buttonRef = useRef(null); const itemsRef = useRef<(HTMLButtonElement | null)[]>([]); // Focus helper const focusItem = (index: number) => { - const visibleItems = itemsRef.current.filter( - Boolean - ) as HTMLButtonElement[]; + const visibleItems = itemsRef.current.filter(Boolean) as HTMLButtonElement[]; if (!visibleItems.length) return; if (index >= visibleItems.length) index = 0; @@ -33,12 +22,10 @@ export function ButtonMenuComponent({ const currentFocusIndex = () => { const visibleItems = itemsRef.current.filter( - (item): item is HTMLButtonElement => item !== null + (item): item is HTMLButtonElement => item !== null, ); const active = document.activeElement; - return active instanceof HTMLButtonElement - ? visibleItems.indexOf(active) - : -1; + return active instanceof HTMLButtonElement ? visibleItems.indexOf(active) : -1; }; const closeMenu = (moveFocus = true) => { @@ -60,9 +47,7 @@ export function ButtonMenuComponent({ const handleKeyDown = (event: KeyboardEvent) => { const { key } = event; const isToggle = event.target === buttonRef.current; - const isMenuItem = itemsRef.current - .filter(Boolean) - .includes(event.target as HTMLButtonElement); + const isMenuItem = itemsRef.current.filter(Boolean).includes(event.target as HTMLButtonElement); if (isToggle) { if (isDisabled) return; @@ -122,10 +107,7 @@ export function ButtonMenuComponent({ return (
- ) + ), )} )} diff --git a/materials_ui/src/components/CaseInfo/CaseInfo.tsx b/materials_ui/src/components/CaseInfo/CaseInfo.tsx index 1578360b..98b5906b 100644 --- a/materials_ui/src/components/CaseInfo/CaseInfo.tsx +++ b/materials_ui/src/components/CaseInfo/CaseInfo.tsx @@ -17,9 +17,7 @@ export const CaseInfo = ({ caseInfo }: Props) => { return null; } - const handleCaseDefendantsLinkClick = ( - event: MouseEvent - ) => { + const handleCaseDefendantsLinkClick = (event: MouseEvent) => { event.preventDefault(); navigate(getRoute('REVIEW_REDACT'), { state: { docType: 'DAC' } }); @@ -30,9 +28,7 @@ export const CaseInfo = ({ caseInfo }: Props) => { ? `, ${caseInfo?.leadDefendantFirstNames}` : ''; const plusNumber = - caseInfo?.numberOfDefendants > 1 - ? ` +${caseInfo?.numberOfDefendants - 1}` - : ''; + caseInfo?.numberOfDefendants > 1 ? ` +${caseInfo?.numberOfDefendants - 1}` : ''; const caseInfoName = `${surname}${firstNames}${plusNumber}`; @@ -49,9 +45,7 @@ export const CaseInfo = ({ caseInfo }: Props) => {
-

- {caseInfoName} -

+

{caseInfoName}

{caseInfo?.urn}

{caseInfo.numberOfDefendants > 0 && (

diff --git a/materials_ui/src/components/Checkbox/Checkbox.tsx b/materials_ui/src/components/Checkbox/Checkbox.tsx index c7fa330e..0dfa8407 100644 --- a/materials_ui/src/components/Checkbox/Checkbox.tsx +++ b/materials_ui/src/components/Checkbox/Checkbox.tsx @@ -17,7 +17,7 @@ export default function Checkbox({ ariaLabel, labelVisuallyHidden, onChange, - value + value, }: Props) { return (

diff --git a/materials_ui/src/components/DateField/DateField.tsx b/materials_ui/src/components/DateField/DateField.tsx index 6616e6c2..87139192 100644 --- a/materials_ui/src/components/DateField/DateField.tsx +++ b/materials_ui/src/components/DateField/DateField.tsx @@ -1,11 +1,5 @@ import dayjs from 'dayjs'; -import { - forwardRef, - useEffect, - useImperativeHandle, - useRef, - useState -} from 'react'; +import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react'; type Props = { id: string; @@ -20,15 +14,9 @@ export const DateField = forwardRef( ({ error, id, hint, label, onChange, value }, ref) => { const initialDate = value && dayjs(value).isValid() ? dayjs(value) : null; - const [day, setDay] = useState( - initialDate ? String(initialDate.date()) : '' - ); - const [month, setMonth] = useState( - initialDate ? String(initialDate.month() + 1) : '' - ); - const [year, setYear] = useState( - initialDate ? String(initialDate.year()) : '' - ); + const [day, setDay] = useState(initialDate ? String(initialDate.date()) : ''); + const [month, setMonth] = useState(initialDate ? String(initialDate.month() + 1) : ''); + const [year, setYear] = useState(initialDate ? String(initialDate.year()) : ''); // Internal refs const dayRef = useRef(null); @@ -48,14 +36,8 @@ export const DateField = forwardRef( }, [day, month, year, onChange]); return ( -
-
+
+
(
-
-
-
); - } + }, ); diff --git a/materials_ui/src/components/DefinitionList/DefinitionList.tsx b/materials_ui/src/components/DefinitionList/DefinitionList.tsx index 5391d456..05ac15ee 100644 --- a/materials_ui/src/components/DefinitionList/DefinitionList.tsx +++ b/materials_ui/src/components/DefinitionList/DefinitionList.tsx @@ -8,9 +8,7 @@ export const DefinitionList = ({ items, fixedWidth }: Props) => { if (!items.length) return null; return ( -
+
{items.map((item, index) => (
{item.title}
diff --git a/materials_ui/src/components/DocumentKeywordSearch/DocumentKeywordSearch.tsx b/materials_ui/src/components/DocumentKeywordSearch/DocumentKeywordSearch.tsx index 8b28341b..e1442e66 100644 --- a/materials_ui/src/components/DocumentKeywordSearch/DocumentKeywordSearch.tsx +++ b/materials_ui/src/components/DocumentKeywordSearch/DocumentKeywordSearch.tsx @@ -7,18 +7,10 @@ import { useDocumentSearchResults, useFilters, usePager, - useSearchTracker + useSearchTracker, } from '../../hooks'; -import { - Banner, - LoadingSpinner, - Modal, - Pagination, - SearchInput, - SectionBreak, - TwoCol -} from '..'; +import { Banner, LoadingSpinner, Modal, Pagination, SearchInput, SectionBreak, TwoCol } from '..'; import { categoriseDocument } from '../../materials_components/DocumentSelectAccordion/utils/categoriseDocument'; import { SearchTermResultType } from '../../schemas/documents'; @@ -29,36 +21,21 @@ import { DocumentKeywordSearchFilters } from '../Filters/DocumentKeywordSearchFi import { DEFAULT_RESULTS_PER_PAGE } from '../../constants/query'; import './DocumentKeywordSearch.scss'; -type DocumentKeywordSearchProps = { - modalOpen: boolean; - setModalOpen: (open: boolean) => void; -}; +type DocumentKeywordSearchProps = { modalOpen: boolean; setModalOpen: (open: boolean) => void }; -export const DocumentKeywordSearch = ({ - modalOpen, - setModalOpen -}: DocumentKeywordSearchProps) => { +export const DocumentKeywordSearch = ({ modalOpen, setModalOpen }: DocumentKeywordSearchProps) => { const { getRoute } = useAppRoute(); const [searchTerm, setSearchTerm] = useState(null); - const [expandedDocuments, setExpandedDocuments] = useState< - Record - >({}); + const [expandedDocuments, setExpandedDocuments] = useState>({}); const [selectedSort, setSelectedSort] = useState('date'); - const { isComplete: trackerComplete, failedToConvert } = - useSearchTracker(searchTerm); + const { isComplete: trackerComplete, failedToConvert } = useSearchTracker(searchTerm); - const { searchResults, loading } = useDocumentSearch( - searchTerm, - trackerComplete - ); + const { searchResults, loading } = useDocumentSearch(searchTerm, trackerComplete); const { documents } = useDocuments(); - const combinedSearchResults = useDocumentSearchResults( - documents ?? [], - searchResults ?? [] - ); + const combinedSearchResults = useDocumentSearchResults(documents ?? [], searchResults ?? []); const { filters, resetFilters } = useFilters('documents'); @@ -84,14 +61,11 @@ export const DocumentKeywordSearch = ({ const newStatus = selectedStatus.includes('New'); - const sortColumn = - selectedSort === 'date' - ? 'cmsFileCreatedDate' - : 'resultsPerDocumentCount'; + const sortColumn = selectedSort === 'date' ? 'cmsFileCreatedDate' : 'resultsPerDocumentCount'; const sortFn = defaultSortFn({ column: sortColumn, - direction: 'descending' + direction: 'descending', }); return combinedSearchResults @@ -107,31 +81,19 @@ export const DocumentKeywordSearch = ({ (category !== null && selectedCategories.includes(category)) ); }) - .map((item) => ({ - ...item, - [sortColumn]: String(item[sortColumn] ?? '') - })) + .map((item) => ({ ...item, [sortColumn]: String(item[sortColumn] ?? '') })) .sort(sortFn); - }, [ - combinedSearchResults, - filters?.filters?.category, - filters?.filters?.status, - selectedSort - ]); + }, [combinedSearchResults, filters?.filters?.category, filters?.filters?.status, selectedSort]); const highlightExactMatches = ( text: string, - words: { boundingBox: number[] | null; text: string; matchType: string[] }[] + words: { boundingBox: number[] | null; text: string; matchType: string }[], ) => { - const exactWords = words - .filter((w) => w.matchType.includes('Exact')) - .map((w) => w.text); + const exactWords = words.filter((w) => w.matchType === 'Exact').map((w) => w.text); if (!text || exactWords.length === 0) return text; - const escaped = exactWords.map((w) => - w.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') - ); + const escaped = exactWords.map((w) => w.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); const regex = new RegExp(`(${escaped.join('|')})`, 'gi'); @@ -142,23 +104,16 @@ export const DocumentKeywordSearch = ({ {part} ) : ( {part} - ) + ), ); }; - const { - currentPage, - totalPages, - startIndex, - endIndex, - setNextPage, - setPage, - setPreviousPage - } = usePager({ - totalItems: filteredResults ? filteredResults.length : 0, - initialPageSize: DEFAULT_RESULTS_PER_PAGE, - initialPage: 0 - }); + const { currentPage, totalPages, startIndex, endIndex, setNextPage, setPage, setPreviousPage } = + usePager({ + totalItems: filteredResults ? filteredResults.length : 0, + initialPageSize: DEFAULT_RESULTS_PER_PAGE, + initialPage: 0, + }); return (
@@ -171,16 +126,11 @@ export const DocumentKeywordSearch = ({ /> - + {trackerComplete && ( setSearchTerm(term)} - /> + setSearchTerm(term)} /> } > {loading &&

Searching…

} @@ -193,16 +143,12 @@ export const DocumentKeywordSearch = ({ {documents?.length} documents in this case

- Search may not have found all instances of "{searchTerm}" in - this case + Search may not have found all instances of "{searchTerm}" in this case

-
@@ -228,10 +172,7 @@ export const DocumentKeywordSearch = ({ type="important" header="Technical problems stopped us from searching these documents:" content={failedToConvert.map((doc: SearchTermResultType) => ( -

+

{doc.presentationTitle}

))} @@ -244,22 +185,15 @@ export const DocumentKeywordSearch = ({ const isExpanded = expandedDocuments[doc.parentId] ?? false; const first = doc.matches[0]; const firstLineMatchCount = - first?.words.filter((word) => - word.matchType?.includes('Exact') - ).length ?? 0; - const remainingCount = - (doc.resultsPerDocumentCount ?? 0) - firstLineMatchCount; + first?.words.filter((word) => word.matchType === 'Exact').length ?? 0; + const remainingCount = (doc.resultsPerDocumentCount ?? 0) - firstLineMatchCount; return (

{doc.documentTitle} @@ -270,24 +204,17 @@ export const DocumentKeywordSearch = ({ Uploaded: {formatDateLong(doc.cmsFileCreatedDate)}

{doc.cmsDocType.documentType && ( -

- Type: {doc.cmsDocType.documentType} -

+

Type: {doc.cmsDocType.documentType}

)}
-

- {first && - highlightExactMatches(first.text, first.words)} -

+

{first && highlightExactMatches(first.text, first.words)}

{isExpanded && remainingCount > 0 && ( <> {doc.matches.slice(1).map((match, index) => (
-

- {highlightExactMatches(match.text, match.words)} -

+

{highlightExactMatches(match.text, match.words)}

))} @@ -299,13 +226,11 @@ export const DocumentKeywordSearch = ({ cursor: 'pointer', textDecoration: 'underline', display: 'inline-block', - marginTop: 8 + marginTop: 8, }} onClick={() => toggleDocumentExpand(doc.parentId)} > - {isExpanded - ? 'Hide additional results' - : `View ${remainingCount} more`} + {isExpanded ? 'Hide additional results' : `View ${remainingCount} more`} )}
diff --git a/materials_ui/src/components/DocumentPreview/DocumentActions.tsx b/materials_ui/src/components/DocumentPreview/DocumentActions.tsx index 99c2de4d..786fbf20 100644 --- a/materials_ui/src/components/DocumentPreview/DocumentActions.tsx +++ b/materials_ui/src/components/DocumentPreview/DocumentActions.tsx @@ -1,10 +1,6 @@ type Props = { label: string; isOpen: boolean; onDocumentOpen: () => void }; -export default function DocumentActions({ - label, - isOpen = false, - onDocumentOpen -}: Props) { +export default function DocumentActions({ label, isOpen = false, onDocumentOpen }: Props) { return ( diff --git a/materials_ui/src/components/DocumentPreview/DocumentPreview.tsx b/materials_ui/src/components/DocumentPreview/DocumentPreview.tsx index 3c4cf482..b35c80d9 100644 --- a/materials_ui/src/components/DocumentPreview/DocumentPreview.tsx +++ b/materials_ui/src/components/DocumentPreview/DocumentPreview.tsx @@ -11,7 +11,7 @@ export default function DocumentPreview({ row }: Props) { const { data: caseDocumentData, loading: caseDocumentLoading, - error: caseDocumentError + error: caseDocumentError, } = useDocumentPreview({ materialId: row.materialId }); const errorTitle = caseDocumentError?.toString().includes('403') @@ -38,10 +38,7 @@ export default function DocumentPreview({ row }: Props) { return ( <> - + {content} ); diff --git a/materials_ui/src/components/Drawer/Drawer.tsx b/materials_ui/src/components/Drawer/Drawer.tsx index ae896fbb..fcaae86c 100644 --- a/materials_ui/src/components/Drawer/Drawer.tsx +++ b/materials_ui/src/components/Drawer/Drawer.tsx @@ -3,10 +3,7 @@ import './Drawer.scss'; type Props = { heading: string }; -export default function Drawer({ - heading, - children -}: PropsWithChildren) { +export default function Drawer({ heading, children }: PropsWithChildren) { return (
diff --git a/materials_ui/src/components/Drawer/RenameDrawer.tsx b/materials_ui/src/components/Drawer/RenameDrawer.tsx index 04386511..383696d6 100644 --- a/materials_ui/src/components/Drawer/RenameDrawer.tsx +++ b/materials_ui/src/components/Drawer/RenameDrawer.tsx @@ -2,6 +2,7 @@ import { ChangeEvent, FormEvent, useState } from 'react'; import { useRename } from '../../hooks'; import { TDocument } from '../../materials_components/DocumentSelectAccordion/getters/getDocumentList'; import { CaseMaterialsType } from '../../schemas'; +import { trackAction } from '../../telemetry/appInsights'; import { LoadingSpinner } from '../LoadingSpinner/LoadingSpinner'; import Drawer from './Drawer'; @@ -13,29 +14,29 @@ type Props = { export const RenameDrawer = ({ material, onCancel, onSuccess }: Props) => { const { isMutating, trigger: renameMaterial } = useRename(material, { - onSuccess + onSuccess: () => { + trackAction('Renamed', { + materialId: + material && 'materialId' in material ? material.materialId?.toString() : undefined, + category: material && 'category' in material ? material.category : undefined, + }); + onSuccess(); + }, }); const [error, setError] = useState(''); - const getDefaultMaterialName = ( - material: CaseMaterialsType | TDocument | null - ) => { + const getDefaultMaterialName = (material: CaseMaterialsType | TDocument | null) => { if (!material) return ''; if ('subject' in material && typeof material.subject === 'string') { return material.subject; } - if ( - 'presentationTitle' in material && - typeof material.presentationTitle === 'string' - ) { + if ('presentationTitle' in material && typeof material.presentationTitle === 'string') { return material.presentationTitle; } return ''; }; - const [inputValue, setInputValue] = useState( - getDefaultMaterialName(material) - ); + const [inputValue, setInputValue] = useState(getDefaultMaterialName(material)); if (!material) return null; @@ -74,23 +75,13 @@ export const RenameDrawer = ({ material, onCancel, onSuccess }: Props) => { return ( - + {!isMutating && (
-
+

-

@@ -103,9 +94,7 @@ export const RenameDrawer = ({ material, onCancel, onSuccess }: Props) => { )} { shallowFilters, setCheckboxFilter, setSearch, - saveFiltersToContext + saveFiltersToContext, } = useFilters('communications'); const hasAccess = useFeatureFlag(); - const handleCheckboxChange = ( - filterGroup: string, - event: ChangeEvent - ) => { + const handleCheckboxChange = (filterGroup: string, event: ChangeEvent) => { const { checked, value } = event.target; setCheckboxFilter(filterGroup, value, checked); @@ -38,22 +35,10 @@ export const CommsFilters = () => { }; const formGroups = [ - { - heading: 'In/Out', - data: ['Incoming', 'Outgoing'], - filterGroup: 'direction' - }, - { - heading: 'Comms type', - data: communicationsCategoryList, - filterGroup: 'method' - }, - { - heading: 'Comms with', - data: communicationsWithList, - filterGroup: 'party' - }, - { heading: 'Type', data: typeList, filterGroup: 'type' } + { heading: 'In/Out', data: ['Incoming', 'Outgoing'], filterGroup: 'direction' }, + { heading: 'Comms type', data: communicationsCategoryList, filterGroup: 'method' }, + { heading: 'Comms with', data: communicationsWithList, filterGroup: 'party' }, + { heading: 'Type', data: typeList, filterGroup: 'type' }, ]; return ( @@ -61,27 +46,20 @@ export const CommsFilters = () => { onSubmit={handleFiltersSubmit} onReset={resetFilters} onSearchChange={handleSearchChange} - searchLabel="Search communications" + searchLabel="Subject" defaultSearchValue={filters?.search || ''} > - {hasAccess([1, 2, 3, 4, 5]) && (
-

- New communication -

+

New communication

handleCheckboxChange('readStatus', event)} value={READ_STATUS.UNREAD} /> @@ -94,21 +72,14 @@ export const CommsFilters = () => {
-

- {heading} -

+

{heading}

{data.map((value) => ( - handleCheckboxChange(`${filterGroup}`, event) - } + checked={shallowFilters?.filters?.[filterGroup]?.includes(value) || false} + onChange={(event) => handleCheckboxChange(`${filterGroup}`, event)} value={value} key={value} /> diff --git a/materials_ui/src/components/Filters/DocumentKeywordSearchFilters.tsx b/materials_ui/src/components/Filters/DocumentKeywordSearchFilters.tsx index be4e3bca..a1e4f11e 100644 --- a/materials_ui/src/components/Filters/DocumentKeywordSearchFilters.tsx +++ b/materials_ui/src/components/Filters/DocumentKeywordSearchFilters.tsx @@ -3,17 +3,15 @@ import { useDocuments, useFilters } from '../../hooks'; import { categoriseDocument } from '../../materials_components/DocumentSelectAccordion/utils/categoriseDocument'; import { categoryDetails, - initDocsOnDocCategoryNamesMap + initDocsOnDocCategoryNamesMap, } from '../../materials_components/DocumentSelectAccordion/utils/categoriseDocumentHelperUtils'; import Checkbox from '../Checkbox/Checkbox'; import { FilterForm } from './FilterForm'; -type DocumentKeywordSearchFiltersProps = { - onSearchSubmit?: (term: string) => void; -}; +type DocumentKeywordSearchFiltersProps = { onSearchSubmit?: (term: string) => void }; export const DocumentKeywordSearchFilters = ({ - onSearchSubmit + onSearchSubmit, }: DocumentKeywordSearchFiltersProps) => { const { filters, @@ -21,15 +19,12 @@ export const DocumentKeywordSearchFilters = ({ shallowFilters, setCheckboxFilter, saveFiltersToContext, - setSearch + setSearch, } = useFilters('documents'); const { documents } = useDocuments(); - const handleCheckboxChange = ( - filterGroup: string, - event: ChangeEvent - ) => { + const handleCheckboxChange = (filterGroup: string, event: ChangeEvent) => { const { checked, value } = event.target; setCheckboxFilter(filterGroup, value, checked); @@ -57,21 +52,9 @@ export const DocumentKeywordSearchFilters = ({ const categoriesList = categoryDetails.map((category) => ({ key: category.categoryName, label: category.label, - documents: docsOnDocCategoryNames[category.categoryName] + documents: docsOnDocCategoryNames[category.categoryName], })); - // const docsByDocType = (() => { - // const map: Record = {}; - - // documents?.forEach((doc) => { - // const type = doc.cmsDocType.documentType; - // if (!type) return; // skip nulls - // map[type] = (map[type] ?? 0) + 1; - // }); - - // return map; - // })(); - return (
-

- New materials -

+

New materials

handleCheckboxChange('status', event)} value={'New'} /> @@ -111,10 +90,7 @@ export const DocumentKeywordSearchFilters = ({ handleCheckboxChange('category', event)} value={category.key} key={category.key} diff --git a/materials_ui/src/components/Filters/FilterForm.tsx b/materials_ui/src/components/Filters/FilterForm.tsx index 363647fa..1be96cc2 100644 --- a/materials_ui/src/components/Filters/FilterForm.tsx +++ b/materials_ui/src/components/Filters/FilterForm.tsx @@ -17,7 +17,7 @@ export const FilterForm = ({ onSearchChange, searchLabel, defaultSearchValue, - children + children, }: FilterFormProps) => { return ( { shallowFilters, setCheckboxFilter, setSearch, - saveFiltersToContext + saveFiltersToContext, } = useFilters('materials'); const hasAccess = useFeatureFlag(); - const handleCheckboxChange = ( - filterGroup: string, - event: ChangeEvent - ) => { + const handleCheckboxChange = (filterGroup: string, event: ChangeEvent) => { const { checked, value } = event.target; setCheckboxFilter(filterGroup, value, checked); @@ -33,7 +30,6 @@ export const MaterialsFilters = () => { setSearch(searchTerm); }; - const categories = materialsCategoryList; const statusList = ['Used', 'Unused']; return ( @@ -41,7 +37,7 @@ export const MaterialsFilters = () => { onSubmit={handleFiltersSubmit} onReset={resetFilters} onSearchChange={handleSearchChange} - searchLabel="Search materials" + searchLabel="Material name" defaultSearchValue={filters?.search || ''} > {hasAccess([2, 3, 4, 5]) && ( @@ -49,18 +45,12 @@ export const MaterialsFilters = () => {
-

- New material -

+

New material

handleCheckboxChange('readStatus', event)} value={READ_STATUS.UNREAD} /> @@ -74,13 +64,12 @@ export const MaterialsFilters = () => {

Status

+ {statusList.map((status) => ( handleCheckboxChange('status', event)} value={status} key={status} @@ -95,16 +84,14 @@ export const MaterialsFilters = () => {

Category

- {categories.map((category) => ( + {materialsCategoryList.map(({ value, label }) => ( handleCheckboxChange('category', event)} - value={category} - key={category} + value={value} + key={value} /> ))}
diff --git a/materials_ui/src/components/Layout/Layout.tsx b/materials_ui/src/components/Layout/Layout.tsx index f4eb44d1..0dc82863 100644 --- a/materials_ui/src/components/Layout/Layout.tsx +++ b/materials_ui/src/components/Layout/Layout.tsx @@ -19,7 +19,7 @@ export const Layout = ({ children, plain = false, title, - shouldBlockNavigationCheck + shouldBlockNavigationCheck, }: PropsWithChildren) => { const { banners } = useBanner(); const { caseInfo, isLoading: caseInfoLoading } = useCaseInfoStore(); @@ -31,36 +31,32 @@ export const Layout = ({ id: 'pcd-request', name: 'PCD Request', href: getRoute('PCD_REQUEST'), - active: - location.pathname === '/' || - location.pathname.includes(getRoute('PCD_REQUEST')) + active: location.pathname === '/' || location.pathname.includes(getRoute('PCD_REQUEST')), }, { id: 'materials', name: 'Materials', href: getRoute('MATERIALS'), - active: location.pathname === getRoute('MATERIALS') + active: location.pathname === getRoute('MATERIALS'), }, { id: 'review-redact', name: 'Review and Redact', href: getRoute('REVIEW_REDACT'), - active: location.pathname === getRoute('REVIEW_REDACT') + active: location.pathname === getRoute('REVIEW_REDACT'), }, { id: 'communications', name: 'Communications', href: getRoute('COMMUNICATIONS'), - active: location.pathname === getRoute('COMMUNICATIONS') + active: location.pathname === getRoute('COMMUNICATIONS'), }, { id: 'pcd-review', name: 'Reviews', href: getRoute('PCD_REVIEW'), - active: - location.pathname === '/' || - location.pathname.includes(getRoute('PCD_REVIEW')) - } + active: location.pathname === '/' || location.pathname.includes(getRoute('PCD_REVIEW')), + }, ]; const tabs = initTabs.map((tab) => ({ ...tab, shouldBlockNavigationCheck })); @@ -74,17 +70,11 @@ export const Layout = ({ return ( <>
-
- {banners && - banners.map((banner, index) => )} -
+
{banners && banners.map((banner, index) => )}
{!plain ? ( <> - + {!caseInfoLoading && caseInfo && ( <> diff --git a/materials_ui/src/components/LoadingSpinner/LoadingSpinner.tsx b/materials_ui/src/components/LoadingSpinner/LoadingSpinner.tsx index 9ff60805..4210964e 100644 --- a/materials_ui/src/components/LoadingSpinner/LoadingSpinner.tsx +++ b/materials_ui/src/components/LoadingSpinner/LoadingSpinner.tsx @@ -1,14 +1,8 @@ import { useRef } from 'react'; -type Props = { - isLoading: boolean; - textContent?: string; -}; +type Props = { isLoading: boolean; textContent?: string }; -export const LoadingSpinner = ({ - isLoading, - textContent = 'Loading...', -}: Props) => { +export const LoadingSpinner = ({ isLoading, textContent = 'Loading...' }: Props) => { const wasLoading = useRef(false); const completeMessage = 'Loading complete.'; diff --git a/materials_ui/src/components/NavList/NavList.scss b/materials_ui/src/components/NavList/NavList.scss index 43efca0a..5314afa5 100644 --- a/materials_ui/src/components/NavList/NavList.scss +++ b/materials_ui/src/components/NavList/NavList.scss @@ -14,4 +14,4 @@ .nav-list-section__heading:first-child { margin-top: 0; -} \ No newline at end of file +} diff --git a/materials_ui/src/components/NavList/NavList.tsx b/materials_ui/src/components/NavList/NavList.tsx index 2c48f30c..98ad076d 100644 --- a/materials_ui/src/components/NavList/NavList.tsx +++ b/materials_ui/src/components/NavList/NavList.tsx @@ -8,11 +8,7 @@ import './NavList.scss'; export type NavListItem = { href: string; name: string }; export type NavListSection = { headerLabel?: string; items: NavListItem[] }; -type Props = { - items?: NavListItem[]; - headerLabel?: string; - sections?: NavListSection[]; -}; +type Props = { items?: NavListItem[]; headerLabel?: string; sections?: NavListSection[] }; export const NavList = ({ items = [], headerLabel, sections }: Props) => { const { pathname } = useLocation(); @@ -23,10 +19,7 @@ export const NavList = ({ items = [], headerLabel, sections }: Props) => { } return ( -
); - } + }, ); diff --git a/materials_ui/src/components/RouteChangeListener/RouteChangeListener.tsx b/materials_ui/src/components/RouteChangeListener/RouteChangeListener.tsx index 38ea0fbb..3de6e74b 100644 --- a/materials_ui/src/components/RouteChangeListener/RouteChangeListener.tsx +++ b/materials_ui/src/components/RouteChangeListener/RouteChangeListener.tsx @@ -3,6 +3,7 @@ import { useLocation } from 'react-router-dom'; import { useBanner } from '../../hooks'; import { useMaterialTags, useSelectedItemsStore } from '../../stores'; +import { trackPageView } from '../../telemetry/appInsights'; // this component is a non rendering component to perform actions when the user // changes pages for example resetting banners, selected items, etc @@ -25,6 +26,7 @@ export const RouteChangeListener = () => { clearSelectedItems(); scrollToTop(); + trackPageView(pathname); }, [pathname]); return null; diff --git a/materials_ui/src/components/SearchInput/SearchInput.tsx b/materials_ui/src/components/SearchInput/SearchInput.tsx index af8618d7..1ae79a1f 100644 --- a/materials_ui/src/components/SearchInput/SearchInput.tsx +++ b/materials_ui/src/components/SearchInput/SearchInput.tsx @@ -17,7 +17,7 @@ export const SearchInput = ({ onSearch, hideButton = true, label, - placeholder + placeholder, }: Props) => { const [searchTerm, setSearchTerm] = useState(''); diff --git a/materials_ui/src/components/SectionBreak/SectionBreak.scss b/materials_ui/src/components/SectionBreak/SectionBreak.scss index 47ef0c0c..8c64a4a2 100644 --- a/materials_ui/src/components/SectionBreak/SectionBreak.scss +++ b/materials_ui/src/components/SectionBreak/SectionBreak.scss @@ -13,5 +13,4 @@ margin-bottom: 30px; margin-top: 30px; } - -} \ No newline at end of file +} diff --git a/materials_ui/src/components/SelectList/SelectList.tsx b/materials_ui/src/components/SelectList/SelectList.tsx index eee2ae2c..73bfc7ef 100644 --- a/materials_ui/src/components/SelectList/SelectList.tsx +++ b/materials_ui/src/components/SelectList/SelectList.tsx @@ -22,26 +22,13 @@ type Props = { export const SelectList = forwardRef( ( - { - id, - defaultValue, - disabled = false, - label, - hint, - error, - options, - onChange, - required, - value - }, - ref + { id, defaultValue, disabled = false, label, hint, error, options, onChange, required, value }, + ref, ) => { const hasError = !!error; return ( -
+
@@ -73,17 +60,12 @@ export const SelectList = forwardRef( required={required} > {options.map(({ value, label, id, disabled = false }) => ( - ))}
); - } + }, ); diff --git a/materials_ui/src/components/SortableTable/CaseMaterialsTable.tsx b/materials_ui/src/components/SortableTable/CaseMaterialsTable.tsx index 33152cf6..ed049225 100644 --- a/materials_ui/src/components/SortableTable/CaseMaterialsTable.tsx +++ b/materials_ui/src/components/SortableTable/CaseMaterialsTable.tsx @@ -4,12 +4,7 @@ import { useSearchParams } from 'react-router-dom'; import { useCaseMaterials, useFilters, usePager } from '../../hooks'; import { CaseMaterialsType } from '../../schemas'; -import { - defaultFilterFn, - defaultSearchFn, - defaultSortFn, - getSortFn -} from '../../utils/filtering'; +import { defaultFilterFn, defaultSearchFn, defaultSortFn, getSortFn } from '../../utils/filtering'; import SortableTable, { Column } from './SortableTable'; import { DEFAULT_RESULTS_PER_PAGE } from '../../constants/query'; @@ -17,12 +12,7 @@ import { DEFAULT_RESULTS_PER_PAGE } from '../../constants/query'; import { READ_STATUS } from '../../constants'; import { formatDate } from '../../utils/date'; -import { - DocumentPreview, - LoadingSpinner, - Pagination, - StatusTag -} from '..'; +import { DocumentPreview, LoadingSpinner, Pagination, StatusTag } from '..'; import { useMaterialTags } from '../../stores'; export const CaseMaterialsTable = () => { @@ -30,98 +20,84 @@ export const CaseMaterialsTable = () => { const { filteredData, loading: caseMaterialsLoading, - error + error, } = useCaseMaterials({ dataType: 'materials' }); const { filters } = useFilters('materials'); const { materialTags } = useMaterialTags(); const columns = useMemo[]>( () => [ - { - key: 'subject', - heading: 'Material', - render: (row) => ( - <> - {row.readStatus == READ_STATUS.UNREAD && } - {row.subject} - {row.statusLabel && } - - ), - isSortable: true - }, - { - key: 'type', - heading: 'Type', - isSortable: true, - sortFn: ({ type: leftType }, { type: rightType }, direction) => { - const compareResult = leftType.localeCompare( - rightType, - undefined, - { numeric: true, sensitivity: 'base' } - ); - return direction === 'ascending' ? compareResult : -compareResult; - } - }, - { key: 'category', heading: 'Category', isSortable: true }, - { - key: 'date', - heading: 'Date', - render: (row) => ( - - {formatDate(row.date)} - - ), - isSortable: true - }, - { - key: 'status', - heading: 'Status', - render: (row) => , - isSortable: true - } - ], - [] + { + key: 'subject', + heading: 'Material name', + render: (row) => ( + <> + {row.readStatus == READ_STATUS.UNREAD && } + {row.subject} + {row.statusLabel && } + + ), + isSortable: true, + }, + { + key: 'type', + heading: 'Type', + isSortable: true, + sortFn: ({ type: leftType }, { type: rightType }, direction) => { + const compareResult = leftType.localeCompare(rightType, undefined, { + numeric: true, + sensitivity: 'base', + }); + return direction === 'ascending' ? compareResult : -compareResult; + }, + }, + { key: 'category', heading: 'Category', isSortable: true }, + { + key: 'date', + heading: 'Date', + render: (row) => ( + + {formatDate(row.date)} + + ), + isSortable: true, + }, + { + key: 'status', + heading: 'Status', + render: (row) => , + isSortable: true, + }, + ], + [], ); const filteredSortedData = useMemo(() => { - const sortFn = getSortFn( - columns, - filters?.sort, - (sortConfig) => defaultSortFn(sortConfig) + const sortFn = getSortFn(columns, filters?.sort, (sortConfig) => + defaultSortFn(sortConfig), ); const sortByStatusFn = defaultSortFn({ column: 'statusLabel', - direction: 'descending' + direction: 'descending', }); const filterFn = defaultFilterFn(filters?.filters); - const searchFn = defaultSearchFn( - 'subject', - filters?.search - ); + const searchFn = defaultSearchFn('subject', filters?.search); return filteredData ?.map((material) => { const materialTag = materialTags.find( - (materialTag) => materialTag.materialId === material.materialId + (materialTag) => materialTag.materialId === material.materialId, ); - return materialTag - ? { ...material, statusLabel: materialTag.tagName } - : material; + return materialTag ? { ...material, statusLabel: materialTag.tagName } : material; }) ?.filter(searchFn) ?.filter(filterFn) ?.sort((a, b) => { - if ( - a.readStatus === READ_STATUS.UNREAD && - b.readStatus !== READ_STATUS.UNREAD - ) { + if (a.readStatus === READ_STATUS.UNREAD && b.readStatus !== READ_STATUS.UNREAD) { return -1; } - if ( - a.readStatus !== READ_STATUS.READ && - b.readStatus === READ_STATUS.READ - ) { + if (a.readStatus !== READ_STATUS.READ && b.readStatus === READ_STATUS.READ) { return 1; } return 0; @@ -132,23 +108,14 @@ export const CaseMaterialsTable = () => { const currentPageParam = queryParams?.get('page'); - const { - currentPage, - totalPages, - startIndex, - endIndex, - setNextPage, - setPage, - setPreviousPage - } = usePager({ - totalItems: filteredSortedData?.length, - initialPageSize: DEFAULT_RESULTS_PER_PAGE, - initialPage: currentPageParam ? +currentPageParam - 1 : 0 - }); - - const expandableRow = (row: CaseMaterialsType) => ( - - ); + const { currentPage, totalPages, startIndex, endIndex, setNextPage, setPage, setPreviousPage } = + usePager({ + totalItems: filteredSortedData?.length, + initialPageSize: DEFAULT_RESULTS_PER_PAGE, + initialPage: currentPageParam ? +currentPageParam - 1 : 0, + }); + + const expandableRow = (row: CaseMaterialsType) => ; const recordsOnCurrentPage = endIndex + 1 - startIndex; @@ -158,43 +125,33 @@ export const CaseMaterialsTable = () => { return ( <> - + {!caseMaterialsLoading && ( - <> -

- Showing{' '} - - {filteredSortedData?.length === 0 ? 0 : recordsOnCurrentPage} - {' '} - materials out of {filteredSortedData?.length} -

- - -
1 ? 'table-actions-footer' : 'table-actions-footer-end' - } - > - -
- + <> +

+ Showing {filteredSortedData?.length === 0 ? 0 : recordsOnCurrentPage}{' '} + materials out of {filteredSortedData?.length} +

+ + +
1 ? 'table-actions-footer' : 'table-actions-footer-end'}> + +
+ )} ); diff --git a/materials_ui/src/components/SortableTable/CommunicationsTable.tsx b/materials_ui/src/components/SortableTable/CommunicationsTable.tsx index ed24328a..93b577d1 100644 --- a/materials_ui/src/components/SortableTable/CommunicationsTable.tsx +++ b/materials_ui/src/components/SortableTable/CommunicationsTable.tsx @@ -3,23 +3,14 @@ import { useSearchParams } from 'react-router-dom'; import { useCaseMaterials, useFilters, usePager } from '../../hooks'; import { CaseMaterialsType } from '../../schemas'; -import { - defaultFilterFn, - defaultSearchFn, - defaultSortFn -} from '../../utils/filtering'; +import { defaultFilterFn, defaultSearchFn, defaultSortFn } from '../../utils/filtering'; import SortableTable, { Column } from './SortableTable'; import { READ_STATUS } from '../../constants'; import { DEFAULT_RESULTS_PER_PAGE } from '../../constants/query'; -import { - DocumentPreview, - LoadingSpinner, - Pagination, - StatusTag -} from '..'; +import { DocumentPreview, LoadingSpinner, Pagination, StatusTag } from '..'; import { useMaterialTags } from '../../stores'; import { formatDate } from '../../utils/date'; @@ -28,7 +19,7 @@ export const CommunicationsTable = () => { const { filteredData, loading: caseMaterialsLoading, - error + error, } = useCaseMaterials({ dataType: 'communications' }); const { filters } = useFilters('communications'); @@ -38,37 +29,26 @@ export const CommunicationsTable = () => { const sortFn = defaultSortFn(filters?.sort); const sortByStatusFn = defaultSortFn({ column: 'statusLabel', - direction: 'descending' + direction: 'descending', }); const filterFn = defaultFilterFn(filters?.filters); - const searchFn = defaultSearchFn( - 'subject', - filters?.search - ); + const searchFn = defaultSearchFn('subject', filters?.search); return filteredData ?.map((material) => { const materialTag = materialTags.find( - (materialTag) => materialTag.materialId === material.materialId + (materialTag) => materialTag.materialId === material.materialId, ); - return materialTag - ? { ...material, statusLabel: materialTag.tagName } - : material; + return materialTag ? { ...material, statusLabel: materialTag.tagName } : material; }) ?.filter(searchFn) ?.filter(filterFn) ?.sort((a, b) => { - if ( - a.readStatus === READ_STATUS.UNREAD && - b.readStatus !== READ_STATUS.UNREAD - ) { + if (a.readStatus === READ_STATUS.UNREAD && b.readStatus !== READ_STATUS.UNREAD) { return -1; } - if ( - a.readStatus !== READ_STATUS.READ && - b.readStatus === READ_STATUS.READ - ) { + if (a.readStatus !== READ_STATUS.READ && b.readStatus === READ_STATUS.READ) { return 1; } return 0; @@ -79,19 +59,12 @@ export const CommunicationsTable = () => { const currentPageParam = queryParams?.get('page'); - const { - currentPage, - totalPages, - startIndex, - endIndex, - setNextPage, - setPage, - setPreviousPage - } = usePager({ - totalItems: filteredSortedData?.length, - initialPageSize: DEFAULT_RESULTS_PER_PAGE, - initialPage: currentPageParam ? +currentPageParam - 1 : 0 - }); + const { currentPage, totalPages, startIndex, endIndex, setNextPage, setPage, setPreviousPage } = + usePager({ + totalItems: filteredSortedData?.length, + initialPageSize: DEFAULT_RESULTS_PER_PAGE, + initialPage: currentPageParam ? +currentPageParam - 1 : 0, + }); const columns: Column[] = [ { @@ -104,7 +77,7 @@ export const CommunicationsTable = () => { {row.statusLabel && } ), - isSortable: true + isSortable: true, }, { key: 'direction', heading: 'In/Out', isSortable: true }, { key: 'party', heading: 'Comms with', isSortable: true }, @@ -118,13 +91,11 @@ export const CommunicationsTable = () => { {formatDate(row.date)} ), - isSortable: true - } + isSortable: true, + }, ]; - const expandableRow = (row: CaseMaterialsType) => ( - - ); + const expandableRow = (row: CaseMaterialsType) => ; const recordsOnCurrentPage = endIndex + 1 - startIndex; @@ -134,44 +105,34 @@ export const CommunicationsTable = () => { return ( <> - + {!caseMaterialsLoading && ( - <> -

- Showing{' '} - - {filteredSortedData?.length === 0 ? 0 : recordsOnCurrentPage} - {' '} - {filteredSortedData?.length === 1 ? `communication` : `communications`}{' '} - out of {filteredSortedData?.length} -

- -
1 ? 'table-actions-footer' : 'table-actions-footer-end' - } - > - -
- + <> +

+ Showing {filteredSortedData?.length === 0 ? 0 : recordsOnCurrentPage}{' '} + {filteredSortedData?.length === 1 ? `communication` : `communications`} out of{' '} + {filteredSortedData?.length} +

+ +
1 ? 'table-actions-footer' : 'table-actions-footer-end'}> + +
+ )} ); diff --git a/materials_ui/src/components/SortableTable/SortableTable.tsx b/materials_ui/src/components/SortableTable/SortableTable.tsx index 46704162..d4d6f5ae 100644 --- a/materials_ui/src/components/SortableTable/SortableTable.tsx +++ b/materials_ui/src/components/SortableTable/SortableTable.tsx @@ -36,24 +36,23 @@ const SortableTable = ({ dataName, checkboxes = true, error, - isCommunications = false + isCommunications = false, }: SortableTableProps) => { const { items: selectedItems, addItems: addSelectedItems, removeItems: removeSelectedItems, - clear: clearSelectedItems + clear: clearSelectedItems, } = useSelectedItemsStore(); const { isPending: isAutoReclassifyPending } = useAutoReclassify(); const { setSort } = useFilters(dataName); - const { selectedMaterialId, selectMaterial, deselectMaterial } = - useCaseMaterial(); + const { selectedMaterialId, selectMaterial, deselectMaterial } = useCaseMaterial(); const materialType = isCommunications ? 'communications' : 'materials'; const handleSelectItem = (material: CaseMaterialsType) => { const isSelected = selectedItems[materialType]?.some( - (m) => m.materialId === material.materialId + (m) => m.materialId === material.materialId, ); if (isSelected) { @@ -86,145 +85,131 @@ const SortableTable = ({ textContent={`Reclassifying ${dataName}...`} /> {!isAutoReclassifyPending && ( -
- - - - - {checkboxes && ( - - )} - {columns.length && - columns.map(({ key, heading, isSortable }) => { - const isSortedColumn = - filters?.sort?.column === key && !!filters?.sort?.direction; - const ariaSortValue = isSortable - ? isSortedColumn && filters?.sort?.direction - ? filters.sort.direction - : 'none' - : undefined; +
+
{caption}
- 0 - } - /> -
+ + + + {checkboxes && ( + + )} + {columns.length && + columns.map(({ key, heading, isSortable }) => { + const isSortedColumn = + filters?.sort?.column === key && !!filters?.sort?.direction; + const ariaSortValue = isSortable + ? isSortedColumn && filters?.sort?.direction + ? filters.sort.direction + : 'none' + : undefined; - return ( - - ); - })} - - - + {isSortable ? ( + + ) : ( + heading + )} + + ); + })} + + + - - {data.length > 0 ? ( - data.map((row, index) => { - const isCurrentMaterial = - selectedMaterialId !== null && - +selectedMaterialId === row.materialId; + + {data.length > 0 ? ( + data.map((row, index) => { + const isCurrentMaterial = + selectedMaterialId !== null && +selectedMaterialId === row.materialId; - return ( - - - {checkboxes && ( - - )} - {columns.map((col, colIndex) => ( - - ))} - - + return ( + + + {checkboxes && ( + + )} + {columns.map((col, colIndex) => ( + + ))} + + - {expandableRow && isCurrentMaterial && ( - - - - )} - - ); - }) - ) : error ? ( - - - - ) : ( - - - - )} - -
{caption}
+ 0 + } + /> + - {isSortable ? ( - - {heading} - - ) : ( - heading - )} -
- m.id === row.id - )} - onChange={() => handleSelectItem(row)} - labelVisuallyHidden={true} - /> - - {/* @ts-expect-error generic type mismatch with CaseMaterialsType */} - {col.render ? col.render(row) : row[col.key]} - - - handleActionsClick( - isCurrentMaterial ? null : row.materialId - ) - } - /> -
+ m.id === row.id)} + onChange={() => handleSelectItem(row)} + labelVisuallyHidden={true} + /> + + {/* @ts-expect-error generic type mismatch with CaseMaterialsType */} + {col.render ? col.render(row) : row[col.key]} + + + handleActionsClick(isCurrentMaterial ? null : row.materialId) + } + /> +
- {expandableRow(row as any)} -
-

- Unable to fetch {dataName} for this case -

-
-

- There are no {dataName} that match your selection for this - case -

-
-
+ {expandableRow && isCurrentMaterial && ( + + + {expandableRow(row as any)} + + + )} + + ); + }) + ) : error ? ( + + +

+ Unable to fetch {dataName} for this case +

+ + + ) : ( + + +

+ There are no {dataName} that match your selection for this case +

+ + + )} + + +
)} ); diff --git a/materials_ui/src/components/SortableTable/TableActions.tsx b/materials_ui/src/components/SortableTable/TableActions.tsx index eb18c8d1..d3202b0f 100644 --- a/materials_ui/src/components/SortableTable/TableActions.tsx +++ b/materials_ui/src/components/SortableTable/TableActions.tsx @@ -17,7 +17,7 @@ export function TableActions({ showFilter, onSetShowFilter, menuItems = [], - selectedItems + selectedItems, }: Props) { const { caseInfo } = useCaseInfoStore(); const [isSticky, setIsSticky] = useState(false); @@ -29,7 +29,7 @@ export function TableActions({ ([entry]) => { if (entry) setIsSticky(!entry.isIntersecting); }, - { threshold: 0 } + { threshold: 0 }, ); if (ref.current) { @@ -44,9 +44,7 @@ export function TableActions({ return ( <>
-
+
{!isSticky && (
@@ -58,7 +56,7 @@ export function TableActions({ onSetShowFilter(!showFilter); log({ logLevel: 1, - message: `HK-UI-FE: caseId [${caseInfo?.id}] - filter panel is now ${filterStatus}.` + message: `HK-UI-FE: caseId [${caseInfo?.id}] - filter panel is now ${filterStatus}.`, }); }} > diff --git a/materials_ui/src/components/SummaryCard/SummaryCard.tsx b/materials_ui/src/components/SummaryCard/SummaryCard.tsx index 08746bba..e3c7be7f 100644 --- a/materials_ui/src/components/SummaryCard/SummaryCard.tsx +++ b/materials_ui/src/components/SummaryCard/SummaryCard.tsx @@ -1,18 +1,8 @@ export type ContentItem = { key: string; value: unknown }; -type Props = { - title: string; - content: ContentItem[]; - actionName?: string; - action?: () => void; -}; +type Props = { title: string; content: ContentItem[]; actionName?: string; action?: () => void }; -export const SummaryCard = ({ - title, - content, - actionName = 'Change', - action -}: Props) => { +export const SummaryCard = ({ title, content, actionName = 'Change', action }: Props) => { return (
diff --git a/materials_ui/src/components/TextArea/TextArea.tsx b/materials_ui/src/components/TextArea/TextArea.tsx index 2cbf31a2..d00b9e38 100644 --- a/materials_ui/src/components/TextArea/TextArea.tsx +++ b/materials_ui/src/components/TextArea/TextArea.tsx @@ -25,9 +25,9 @@ export const TextArea = forwardRef( onChange, required, rows = 5, - maxCharacters = 0 + maxCharacters = 0, }, - ref + ref, ) => { const [text, setText] = useState(''); const hasError = !!error; @@ -38,9 +38,7 @@ export const TextArea = forwardRef( }; return ( -
+
@@ -74,14 +72,11 @@ export const TextArea = forwardRef( /> {!!maxCharacters && ( -
+
You can enter up to {maxCharacters - text.length} characters
)}
); - } + }, ); diff --git a/materials_ui/src/components/TextInput/TextInput.tsx b/materials_ui/src/components/TextInput/TextInput.tsx index e928e8d2..fba692cb 100644 --- a/materials_ui/src/components/TextInput/TextInput.tsx +++ b/materials_ui/src/components/TextInput/TextInput.tsx @@ -35,16 +35,14 @@ export const TextInput = forwardRef( type = 'text', autocomplete, spellCheck, - readonly = false + readonly = false, }, - ref + ref, ) => { const hasError = !!error; return ( -
+
@@ -82,5 +80,5 @@ export const TextInput = forwardRef( />
); - } + }, ); diff --git a/materials_ui/src/components/forms/EditMaterial/EditExhibit.tsx b/materials_ui/src/components/forms/EditMaterial/EditExhibit.tsx index 362fffd1..bde49177 100644 --- a/materials_ui/src/components/forms/EditMaterial/EditExhibit.tsx +++ b/materials_ui/src/components/forms/EditMaterial/EditExhibit.tsx @@ -3,10 +3,7 @@ import { Controller, useForm } from 'react-hook-form'; import { Link } from 'react-router-dom'; import { useExhibitProducers } from '../../../hooks/index.ts'; -import { - EditExhibitSchema, - EditExhibitType -} from '../../../schemas/forms/editStatement.ts'; +import { EditExhibitSchema, EditExhibitType } from '../../../schemas/forms/editStatement.ts'; import { CaseMaterialsType } from '../../../schemas/index.ts'; import { SelectList } from '../../SelectList/SelectList.tsx'; @@ -21,22 +18,15 @@ type Props = { onSuccess: (data: EditExhibitType) => void; }; -export const EditExhibitForm = ({ - cancelUrl, - formState, - material, - onSuccess -}: Props) => { - const { - selectOptions: exhibitProducers, - loading: isExhibitProducersLoading - } = useExhibitProducers(); +export const EditExhibitForm = ({ cancelUrl, formState, material, onSuccess }: Props) => { + const { selectOptions: exhibitProducers, loading: isExhibitProducersLoading } = + useExhibitProducers(); const { control, handleSubmit, formState: { errors }, - watch + watch, } = useForm({ // @ts-expect-error fix type resolver: zodResolver(EditExhibitSchema), @@ -48,14 +38,11 @@ export const EditExhibitForm = ({ subject: formState?.item || material?.subject || '', used: formState?.used || material?.status === 'Used', existingproducerOrWitnessId: - formState?.existingproducerOrWitnessId || - material?.existingproducerOrWitnessId, + formState?.existingproducerOrWitnessId || material?.existingproducerOrWitnessId, producedBy: formState?.producedBy || - (!material?.existingproducerOrWitnessId - ? material?.producer?.trim() - : undefined) - } + (!material?.existingproducerOrWitnessId ? material?.producer?.trim() : undefined), + }, }); const fieldValues = watch(); @@ -126,10 +113,8 @@ export const EditExhibitForm = ({ options={[ ...(isExhibitProducersLoading ? [{ label: 'Loading...', value: '', id: '' }] - : [ - { label: 'Select producer or witness', value: '', id: '' } - ]), - ...exhibitProducers + : [{ label: 'Select producer or witness', value: '', id: '' }]), + ...exhibitProducers, ]} /> )} @@ -154,11 +139,7 @@ export const EditExhibitForm = ({
- diff --git a/materials_ui/src/components/forms/EditMaterial/EditStatement.tsx b/materials_ui/src/components/forms/EditMaterial/EditStatement.tsx index 172b75dc..f5a0df57 100644 --- a/materials_ui/src/components/forms/EditMaterial/EditStatement.tsx +++ b/materials_ui/src/components/forms/EditMaterial/EditStatement.tsx @@ -3,14 +3,8 @@ import { useEffect } from 'react'; import { Controller, useForm } from 'react-hook-form'; import { Link } from 'react-router-dom'; -import { - useCaseWitnesses, - useWitnessStatements -} from '../../../hooks/index.ts'; -import { - EditStatementSchema, - EditStatementType -} from '../../../schemas/forms/editStatement.ts'; +import { useCaseWitnesses, useWitnessStatements } from '../../../hooks/index.ts'; +import { EditStatementSchema, EditStatementType } from '../../../schemas/forms/editStatement.ts'; import { CaseMaterialsType } from '../../../schemas/index.ts'; import { SelectList } from '../../SelectList/SelectList.tsx'; @@ -26,21 +20,16 @@ type Props = { cancelUrl: string; }; -export const EditStatementForm = ({ - formState, - material, - onSuccess, - cancelUrl -}: Props) => { +export const EditStatementForm = ({ formState, material, onSuccess, cancelUrl }: Props) => { const { selectOptions, loading: isWitnessesLoading } = useCaseWitnesses(); const { data: witnessStatements, loading: isWitnessStatementsLoading, - setWitnessId + setWitnessId, } = useWitnessStatements(); const matchedStatement = witnessStatements.find( - (statement) => statement.id === material.materialId + (statement) => statement.id === material.materialId, ); const defaultStatementNumber = matchedStatement @@ -51,21 +40,18 @@ export const EditStatementForm = ({ control, handleSubmit, formState: { errors }, - watch + watch, } = useForm({ // @ts-expect-error fix type here resolver: zodResolver(EditStatementSchema), defaultValues: { - hasStatementDate: formState - ? formState?.hasStatementDate - : !!material?.recordedDate, + hasStatementDate: formState ? formState?.hasStatementDate : !!material?.recordedDate, materialId: material?.materialId, - statementDate: - formState?.statementDate || material?.recordedDate || undefined, + statementDate: formState?.statementDate || material?.recordedDate || undefined, statementNumber: formState?.statementNumber || undefined, used: formState ? formState.used : material?.status === 'Used', - witnessId: formState?.witnessId || material?.witnessId || undefined - } + witnessId: formState?.witnessId || material?.witnessId || undefined, + }, }); const fieldValues = watch(); @@ -122,12 +108,8 @@ export const EditStatementForm = ({ label="Who is the witness?" error={errors?.witnessId?.message as string} options={[ - { - label: isWitnessesLoading ? 'Loading...' : 'Select witness', - value: '', - id: '' - }, - ...selectOptions + { label: isWitnessesLoading ? 'Loading...' : 'Select witness', value: '', id: '' }, + ...selectOptions, ]} /> )} @@ -139,18 +121,12 @@ export const EditStatementForm = ({ render={({ field }) => (
- diff --git a/materials_ui/src/components/forms/EditMaterial/Summary.tsx b/materials_ui/src/components/forms/EditMaterial/Summary.tsx index e004e10b..416f1138 100644 --- a/materials_ui/src/components/forms/EditMaterial/Summary.tsx +++ b/materials_ui/src/components/forms/EditMaterial/Summary.tsx @@ -1,6 +1,6 @@ -import { type ContentItem, SummaryCard } from '../../SummaryCard/SummaryCard.tsx'; import { Link } from 'react-router-dom'; import { URL } from '../../../constants/url.ts'; +import { type ContentItem, SummaryCard } from '../../SummaryCard/SummaryCard.tsx'; type Props = { onChange: () => void; @@ -9,12 +9,7 @@ type Props = { title: string; }; -export const Summary = ({ - onChange, - onSave, - summaryCardData, - title -}: Props) => { +export const Summary = ({ onChange, onSave, summaryCardData, title }: Props) => { const handleChangeClick = () => { onChange(); }; @@ -27,18 +22,10 @@ export const Summary = ({ <>

Check your answers

- +
- diff --git a/materials_ui/src/components/forms/Reclassify/AddWitness.tsx b/materials_ui/src/components/forms/Reclassify/AddWitness.tsx index 5bbe563e..8f7aa2c9 100644 --- a/materials_ui/src/components/forms/Reclassify/AddWitness.tsx +++ b/materials_ui/src/components/forms/Reclassify/AddWitness.tsx @@ -3,15 +3,15 @@ import dayjs from 'dayjs'; import { Controller, useForm } from 'react-hook-form'; import { Link } from 'react-router-dom'; -import { ErrorSummary, Radios, SelectList, TextArea, TextInput } from '../../index.ts'; import { URL } from '../../../constants/url.ts'; import { useCaseDefendants } from '../../../hooks/index.ts'; import { Reclassify_WitnessAndActionPlanSchema, - type Reclassify_WitnessAndActionPlanType + type Reclassify_WitnessAndActionPlanType, } from '../../../schemas/forms/reclassify.ts'; import { formatDateInputValue } from '../../../utils/date.ts'; import type { ErrorSummaryItem } from '../../ErrorSummary/ErrorSummary.tsx'; +import { ErrorSummary, Radios, SelectList, TextArea, TextInput } from '../../index.ts'; type Props = { data: Reclassify_WitnessAndActionPlanType; @@ -24,11 +24,11 @@ export const AddWitness = ({ data, onSave }: Props) => { control, formState: { errors }, handleSubmit, - watch + watch, } = useForm({ defaultValues: data, // @ts-expect-error fix type - resolver: zodResolver(Reclassify_WitnessAndActionPlanSchema) + resolver: zodResolver(Reclassify_WitnessAndActionPlanSchema), }); // format react-hook-form error object into array to be rendered by ErrorSummary @@ -104,7 +104,7 @@ export const AddWitness = ({ data, onSave }: Props) => { legend="What do you want to request?" options={[ { id: 'KWD', value: 'KWD', label: 'Key witness details' }, - { id: 'NKWD', value: 'NKWD', label: 'Non-key witness details' } + { id: 'NKWD', value: 'NKWD', label: 'Non-key witness details' }, ]} required error={errors?.requestType?.message as string} @@ -128,7 +128,7 @@ export const AddWitness = ({ data, onSave }: Props) => { ...defendantSelectOptions, ...(defendantSelectOptions?.length > 1 ? [{ label: 'All defendants', value: 0, id: 0 }] - : []) + : []), ]} /> )} @@ -174,18 +174,12 @@ export const AddWitness = ({ data, onSave }: Props) => { render={({ field }) => ( { )}
- diff --git a/materials_ui/src/components/forms/Reclassify/Exhibit.tsx b/materials_ui/src/components/forms/Reclassify/Exhibit.tsx index 5df8db94..3533ae05 100644 --- a/materials_ui/src/components/forms/Reclassify/Exhibit.tsx +++ b/materials_ui/src/components/forms/Reclassify/Exhibit.tsx @@ -15,10 +15,8 @@ type Props = { }; export const Exhibit = ({ control, data, errors, currentMaterial }: Props) => { - const { - selectOptions: exhibitProducerOptions, - loading: isExhibitProducersLoading - } = useExhibitProducers(); + const { selectOptions: exhibitProducerOptions, loading: isExhibitProducersLoading } = + useExhibitProducers(); return ( <> @@ -43,11 +41,7 @@ export const Exhibit = ({ control, data, errors, currentMaterial }: Props) => { )} /> - + { ...(isExhibitProducersLoading ? [{ label: 'Loading...', value: '', id: '' }] : [{ label: 'Select producer or witness', value: '', id: '' }]), - ...exhibitProducerOptions + ...exhibitProducerOptions, ]} /> )} diff --git a/materials_ui/src/components/forms/Reclassify/MGForms.tsx b/materials_ui/src/components/forms/Reclassify/MGForms.tsx index 24a4d713..a2632efe 100644 --- a/materials_ui/src/components/forms/Reclassify/MGForms.tsx +++ b/materials_ui/src/components/forms/Reclassify/MGForms.tsx @@ -5,11 +5,7 @@ import { DocumentTypeField } from './common/DocumentTypeField'; import { SubjectField } from './common/SubjectField'; import { UsedField } from './common/UsedField'; -type Props = { - control: Control; - errors?: FieldErrors; - currentMaterial: CaseMaterialsType; -}; +type Props = { control: Control; errors?: FieldErrors; currentMaterial: CaseMaterialsType }; export const MGForms = ({ control, errors, currentMaterial }: Props) => { return ( diff --git a/materials_ui/src/components/forms/Reclassify/MaterialName.tsx b/materials_ui/src/components/forms/Reclassify/MaterialName.tsx index ec679bfc..16a3c8ef 100644 --- a/materials_ui/src/components/forms/Reclassify/MaterialName.tsx +++ b/materials_ui/src/components/forms/Reclassify/MaterialName.tsx @@ -6,24 +6,21 @@ import { ErrorSummary } from '../..'; import { URL } from '../../../constants/url'; import { Reclassify_MaterialNameFormSchema, - Reclassify_MaterialNameFormType + Reclassify_MaterialNameFormType, } from '../../../schemas/forms/reclassify'; import type { ErrorSummaryItem } from '../../ErrorSummary/ErrorSummary'; import { SubjectField } from './common/SubjectField'; -type Props = { - data: Record; - onSave: (data: Record) => void; -}; +type Props = { data: Record; onSave: (data: Record) => void }; export const MaterialName = ({ data, onSave }: Props) => { const { control, formState: { errors }, - handleSubmit + handleSubmit, } = useForm({ defaultValues: data, - resolver: zodResolver(Reclassify_MaterialNameFormSchema) + resolver: zodResolver(Reclassify_MaterialNameFormSchema), }); // format react-hook-form error object into array to be rendered by ErrorSummary @@ -52,11 +49,7 @@ export const MaterialName = ({ data, onSave }: Props) => {
- diff --git a/materials_ui/src/components/forms/Reclassify/Other.tsx b/materials_ui/src/components/forms/Reclassify/Other.tsx index ecf3bd28..6cbc9cc6 100644 --- a/materials_ui/src/components/forms/Reclassify/Other.tsx +++ b/materials_ui/src/components/forms/Reclassify/Other.tsx @@ -5,11 +5,7 @@ import { DocumentTypeField } from './common/DocumentTypeField'; import { SubjectField } from './common/SubjectField'; import { UsedField } from './common/UsedField'; -type Props = { - control: Control; - errors?: FieldErrors; - currentMaterial: CaseMaterialsType; -}; +type Props = { control: Control; errors?: FieldErrors; currentMaterial: CaseMaterialsType }; export const Other = ({ control, errors, currentMaterial }: Props) => { return ( diff --git a/materials_ui/src/components/forms/Reclassify/Statement.tsx b/materials_ui/src/components/forms/Reclassify/Statement.tsx index a382b0eb..ed1aa851 100644 --- a/materials_ui/src/components/forms/Reclassify/Statement.tsx +++ b/materials_ui/src/components/forms/Reclassify/Statement.tsx @@ -5,15 +5,10 @@ import type { ReclassifyFormData } from '../../../hooks'; import { useCaseWitnesses, useWitnessStatements } from '../../../hooks'; import { UsedField } from './common/UsedField'; -type Props = { - control: Control; - errors?: FieldErrors; - data?: ReclassifyFormData; -}; +type Props = { control: Control; errors?: FieldErrors; data?: ReclassifyFormData }; export const Statement = ({ control, data, errors }: Props) => { - const { selectOptions: witnessOptions, loading: isWitnessesLoading } = - useCaseWitnesses(); + const { selectOptions: witnessOptions, loading: isWitnessesLoading } = useCaseWitnesses(); const { data: witnessStatements, setWitnessId } = useWitnessStatements(); if (data?.classification !== 'STATEMENT') { @@ -65,20 +60,14 @@ export const Statement = ({ control, data, errors }: Props) => { ? [{ label: 'Loading...', value: '', id: '' }] : [{ label: 'Select witness', value: '', id: '' }]), ...witnessOptions, - { - label: 'Witness not on the list - add witness', - value: '0', - id: '0' - } + { label: 'Witness not on the list - add witness', value: '0', id: '0' }, ]} /> )} /> {data?.witnessId === 0 && (

- - You will be asked to add a new witness on the next page. - + You will be asked to add a new witness on the next page.

)} @@ -88,18 +77,12 @@ export const Statement = ({ control, data, errors }: Props) => { render={({ field }) => ( { const documentType = getDocumentTypeById(data?.documentType as number); const usedStatus = data?.used ? 'Used' : 'Unused'; const classification = mapBEClassificationToFE( - data?.classification as Reclassify_ClassificationForm['classification'] + data?.classification as Reclassify_ClassificationForm['classification'], ); const witnessName = data?.classification === 'STATEMENT' @@ -53,15 +53,11 @@ export const Summary = ({ data, onChange, onSave }: Props) => { data?.classification === 'STATEMENT' ? data?.witnessActionPlan?.defendantId === 0 ? 'All defendants' - : formatDefendantName( - getDefendantById(data?.witnessActionPlan?.defendantId as number) - ) + : formatDefendantName(getDefendantById(data?.witnessActionPlan?.defendantId as number)) : null; const exhibitProducer = - data?.classification === 'EXHIBIT' - ? getExhibitProducerById(data?.producerId) - : false; + data?.classification === 'EXHIBIT' ? getExhibitProducerById(data?.producerId) : false; return ( <> @@ -77,8 +73,8 @@ export const Summary = ({ data, onChange, onSave }: Props) => {
{documentType?.name} - ) - } + ), + }, ]} /> @@ -88,29 +84,21 @@ export const Summary = ({ data, onChange, onSave }: Props) => { action={() => handleChangeClick('classification')} title={`${documentType?.name} details`} content={[ - { - key: 'Who is the Witness', - value: witnessName || 'New witness (see below)' - }, + { key: 'Who is the Witness', value: witnessName || 'New witness (see below)' }, { key: 'Does the statement have a date?', - value: data?.hasStatementDate ? 'Yes' : 'No' + value: data?.hasStatementDate ? 'Yes' : 'No', }, ...(data?.hasStatementDate ? [ { key: 'What is the statement date?', - value: data?.statementDate - ? formatDate(data?.statementDate) - : '' - } + value: data?.statementDate ? formatDate(data?.statementDate) : '', + }, ] : []), - { - key: 'Statement number', - value: data?.statementNumber as number - }, - { key: 'What is the material status?', value: usedStatus } + { key: 'Statement number', value: data?.statementNumber as number }, + { key: 'What is the material status?', value: usedStatus }, ]} /> @@ -121,41 +109,29 @@ export const Summary = ({ data, onChange, onSave }: Props) => { content={[ { key: 'Name', value: data?.witnessActionPlan?.firstName }, { key: 'Surname', value: data?.witnessActionPlan?.surname }, - { - key: 'Contested issue', - value: data?.witnessActionPlan?.actionPointText - }, + { key: 'Contested issue', value: data?.witnessActionPlan?.actionPointText }, { key: 'What do you want to request', value: data?.witnessActionPlan?.requestType === 'KWD' ? 'Key witness details' - : 'Non-key witness details' - }, - { - key: 'Select the defendant the action plan relates to', - value: defendantName - }, - { - key: 'Describe the action plan', - value: data?.witnessActionPlan?.actionPlan - }, - { - key: 'Date needed', - value: formatDate(data?.witnessActionPlan?.dateNeeded) + : 'Non-key witness details', }, + { key: 'Select the defendant the action plan relates to', value: defendantName }, + { key: 'Describe the action plan', value: data?.witnessActionPlan?.actionPlan }, + { key: 'Date needed', value: formatDate(data?.witnessActionPlan?.dateNeeded) }, { key: 'Do you want to add a follow up?', - value: data?.witnessActionPlan?.followUp ? 'Yes' : 'No' + value: data?.witnessActionPlan?.followUp ? 'Yes' : 'No', }, ...(data?.witnessActionPlan?.followUp ? [ { key: 'Follow up date', - value: formatDate(data?.witnessActionPlan?.followUpDate) - } + value: formatDate(data?.witnessActionPlan?.followUpDate), + }, ] - : []) + : []), ]} /> )} @@ -177,13 +153,11 @@ export const Summary = ({ data, onChange, onSave }: Props) => { ? [ { key: 'Exhibit producer', - value: exhibitProducer - ? exhibitProducer?.producer - : data?.producedBy - } + value: exhibitProducer ? exhibitProducer?.producer : data?.producedBy, + }, ] : []), - { key: 'What is the material status?', value: usedStatus } + { key: 'What is the material status?', value: usedStatus }, ]} /> @@ -192,24 +166,18 @@ export const Summary = ({ data, onChange, onSave }: Props) => { {['MG Form', 'OTHER'].includes(data.classification as string) && ( - handleChangeClick( - data.classification === 'STATEMENT' ? 'subject' : 'classification' - ) + handleChangeClick(data.classification === 'STATEMENT' ? 'subject' : 'classification') } title={`${documentType?.name} details`} content={[ { key: 'Material name', value: data?.subject }, - { key: 'What is the material status?', value: usedStatus } + { key: 'What is the material status?', value: usedStatus }, ]} /> )}
- diff --git a/materials_ui/src/components/forms/Reclassify/common/DocumentTypeField.tsx b/materials_ui/src/components/forms/Reclassify/common/DocumentTypeField.tsx index a0681486..0a0e0cc6 100644 --- a/materials_ui/src/components/forms/Reclassify/common/DocumentTypeField.tsx +++ b/materials_ui/src/components/forms/Reclassify/common/DocumentTypeField.tsx @@ -12,21 +12,16 @@ type Props = { excludedTypeIds: (string | number)[]; }; -export const DocumentTypeField = ({ - control, - errors, - type, - excludedTypeIds = [] -}: Props) => { +export const DocumentTypeField = ({ control, errors, type, excludedTypeIds = [] }: Props) => { const { selectOptions } = useDocumentTypes(); const filteredOptions = useMemo( () => selectOptions(type).map((option) => ({ ...option, - disabled: excludedTypeIds?.includes(option?.id) + disabled: excludedTypeIds?.includes(option?.id), })), - [excludedTypeIds, selectOptions, type] + [excludedTypeIds, selectOptions, type], ); return ( @@ -40,10 +35,7 @@ export const DocumentTypeField = ({ label="What is the material classification type?" error={errors?.documentType?.message as string} defaultValue={field?.value?.toString()} - options={[ - { label: 'Select type', value: '', id: '' }, - ...filteredOptions - ]} + options={[{ label: 'Select type', value: '', id: '' }, ...filteredOptions]} onChange={(value) => { field.onChange(value || undefined); }} diff --git a/materials_ui/src/components/forms/Reclassify/common/SubjectField.tsx b/materials_ui/src/components/forms/Reclassify/common/SubjectField.tsx index e88f5ed0..acb8b9a2 100644 --- a/materials_ui/src/components/forms/Reclassify/common/SubjectField.tsx +++ b/materials_ui/src/components/forms/Reclassify/common/SubjectField.tsx @@ -1,10 +1,4 @@ -import { - Control, - Controller, - FieldErrors, - FieldValues, - Path -} from 'react-hook-form'; +import { Control, Controller, FieldErrors, FieldValues, Path } from 'react-hook-form'; import { TextInput } from '../../..'; type Props = { @@ -18,7 +12,7 @@ export const SubjectField = ({ control, defaultValue, errors, - label + label, }: Props) => { return ( { render={({ field }) => ( , errors: FieldErrors, - caseMaterial: CaseMaterialsType + caseMaterial: CaseMaterialsType, ): RadioOption[] => [ { label: 'Statement (MG11)', @@ -18,49 +18,32 @@ export const categoryOptions = ( // prevent user trying to reclassify a statement to a statement disabled: caseMaterial.category === 'Statement', conditionalField: ( - - ) + + ), }, { label: 'Exhibit', value: 'EXHIBIT', id: 'exhibit', conditionalField: ( - - ) + + ), }, { label: 'MG Forms', value: 'MG Form', id: 'mgForm', - conditionalField: ( - - ) + conditionalField: , }, { label: 'Other', value: 'OTHER', id: 'other', - conditionalField: ( - - ) - } + conditionalField: , + }, ]; export const usedOptions: RadioOption[] = [ { label: 'Used', value: 'true', id: 'used' }, - { label: 'Unused', value: 'false', id: 'unused' } + { label: 'Unused', value: 'false', id: 'unused' }, ]; diff --git a/materials_ui/src/components/forms/Reclassify/constants/string.ts b/materials_ui/src/components/forms/Reclassify/constants/string.ts index fbb37008..469b9dc2 100644 --- a/materials_ui/src/components/forms/Reclassify/constants/string.ts +++ b/materials_ui/src/components/forms/Reclassify/constants/string.ts @@ -3,15 +3,10 @@ import { Reclassify_ClassificationForm } from '../../../../schemas/forms/reclass const BE_TO_FE_CLASSIFICATIONS_MAP: Record< Reclassify_ClassificationForm['classification'], string -> = { - STATEMENT: 'Statement', - EXHIBIT: 'Exhibit', - 'MG Form': 'MG Forms', - OTHER: 'Other' -}; +> = { STATEMENT: 'Statement', EXHIBIT: 'Exhibit', 'MG Form': 'MG Forms', OTHER: 'Other' }; export const mapBEClassificationToFE = ( - classification: Reclassify_ClassificationForm['classification'] + classification: Reclassify_ClassificationForm['classification'], ) => { return BE_TO_FE_CLASSIFICATIONS_MAP[classification]; }; diff --git a/materials_ui/src/components/forms/Reclassify/mappers/mapReclassifyExhibit.ts b/materials_ui/src/components/forms/Reclassify/mappers/mapReclassifyExhibit.ts index aebb9c8c..6282cb2b 100644 --- a/materials_ui/src/components/forms/Reclassify/mappers/mapReclassifyExhibit.ts +++ b/materials_ui/src/components/forms/Reclassify/mappers/mapReclassifyExhibit.ts @@ -3,7 +3,7 @@ import type { Reclassify_Orchestrated_Request_Type } from '../../../../schemas/f export const mapReclassifyExhibit = ( data: ReclassifyFormData, - urn: string + urn: string, ): Reclassify_Orchestrated_Request_Type => { if (data.classification !== 'EXHIBIT') { throw new Error('Not a valid classification'); @@ -19,13 +19,9 @@ export const mapReclassifyExhibit = ( exhibit: { item: data?.item, reference: data?.referenceNumber, - ...(data?.producerId - ? { existingproducerOrWitnessId: data?.producerId as number } - : {}), - ...(data?.producedBy - ? { Producer: data?.producedBy, newProducer: data?.producedBy } - : {}) - } - } + ...(data?.producerId ? { existingproducerOrWitnessId: data?.producerId as number } : {}), + ...(data?.producedBy ? { Producer: data?.producedBy, newProducer: data?.producedBy } : {}), + }, + }, }; }; diff --git a/materials_ui/src/components/forms/Reclassify/mappers/mapReclassifyMGForm.ts b/materials_ui/src/components/forms/Reclassify/mappers/mapReclassifyMGForm.ts index ffc87fbb..8b802178 100644 --- a/materials_ui/src/components/forms/Reclassify/mappers/mapReclassifyMGForm.ts +++ b/materials_ui/src/components/forms/Reclassify/mappers/mapReclassifyMGForm.ts @@ -3,7 +3,7 @@ import type { Reclassify_Orchestrated_Request_Type } from '../../../../schemas/f export const mapReclassifyMGForm = ( data: ReclassifyFormData, - urn: string + urn: string, ): Reclassify_Orchestrated_Request_Type => { if (data.classification !== 'MG Form') { throw new Error('Not a valid classification'); @@ -15,7 +15,7 @@ export const mapReclassifyMGForm = ( classification: 'OTHER', documentTypeId: data?.documentType, subject: data?.subject, - used: data?.used - } + used: data?.used, + }, }; }; diff --git a/materials_ui/src/components/forms/Reclassify/mappers/mapReclassifyOther.ts b/materials_ui/src/components/forms/Reclassify/mappers/mapReclassifyOther.ts index 99510ed9..35d40b64 100644 --- a/materials_ui/src/components/forms/Reclassify/mappers/mapReclassifyOther.ts +++ b/materials_ui/src/components/forms/Reclassify/mappers/mapReclassifyOther.ts @@ -3,7 +3,7 @@ import type { Reclassify_Orchestrated_Request_Type } from '../../../../schemas/f export const mapReclassifyOther = ( data: ReclassifyFormData, - urn: string + urn: string, ): Reclassify_Orchestrated_Request_Type => { if (data.classification !== 'OTHER') { throw new Error('Not a valid classification'); @@ -15,7 +15,7 @@ export const mapReclassifyOther = ( classification: 'OTHER', documentTypeId: data?.documentType, subject: data?.subject, - used: data?.used - } + used: data?.used, + }, }; }; diff --git a/materials_ui/src/components/forms/Reclassify/mappers/mapReclassifyStatement.ts b/materials_ui/src/components/forms/Reclassify/mappers/mapReclassifyStatement.ts index b195ef89..a716b261 100644 --- a/materials_ui/src/components/forms/Reclassify/mappers/mapReclassifyStatement.ts +++ b/materials_ui/src/components/forms/Reclassify/mappers/mapReclassifyStatement.ts @@ -3,7 +3,7 @@ import dayjs from 'dayjs'; import type { ReclassifyFormData } from '../../../../hooks'; import { type Reclassify_Orchestrated_Request_Type, - Reclassify_RequestTypeEnum + Reclassify_RequestTypeEnum, } from '../../../../schemas/forms/reclassify'; import { formatDateInputValue } from '../../../../utils/date'; @@ -11,7 +11,7 @@ const dateFormat = 'YYYY-MM-DD'; export const mapReclassifyStatement = ( data: ReclassifyFormData, - urn: string + urn: string, ): Reclassify_Orchestrated_Request_Type => { if (data.classification !== 'STATEMENT') { throw new Error('Not a valid classification'); @@ -46,8 +46,8 @@ export const mapReclassifyStatement = ( description: 'Key Witness Details', text: '', hidden: false, - hiddenDraft: false - } + hiddenDraft: false, + }, ] : [ { @@ -55,10 +55,10 @@ export const mapReclassifyStatement = ( description: 'Non-Key Witness Details', text: '', hidden: false, - hiddenDraft: false - } - ]) - ] + hiddenDraft: false, + }, + ]), + ], }; const witness: Reclassify_Orchestrated_Request_Type['witness'] = { @@ -66,8 +66,8 @@ export const mapReclassifyStatement = ( ? { witnessId: data?.witnessId } : { firstName: witnessActionPlan?.firstName || '', - surname: witnessActionPlan?.surname || '' - }) + surname: witnessActionPlan?.surname || '', + }), }; return { @@ -79,12 +79,10 @@ export const mapReclassifyStatement = ( used: data?.used, statement: { statementNo: data?.statementNumber, - ...(data?.hasStatementDate - ? { date: formatDateInputValue(data?.statementDate) } - : {}) - } + ...(data?.hasStatementDate ? { date: formatDateInputValue(data?.statementDate) } : {}), + }, }, witness, - ...(hasActionPlan ? { actionPlan: actionPlanData } : {}) + ...(hasActionPlan ? { actionPlan: actionPlanData } : {}), }; }; diff --git a/materials_ui/src/components/forms/Reclassify/utils/form.ts b/materials_ui/src/components/forms/Reclassify/utils/form.ts index 9f669502..74c9f75d 100644 --- a/materials_ui/src/components/forms/Reclassify/utils/form.ts +++ b/materials_ui/src/components/forms/Reclassify/utils/form.ts @@ -2,7 +2,7 @@ export const generateMaterialName = ( documentType: string, firstName: string, surname: string, - date?: string + date?: string, ) => { return `${documentType} ${surname.toUpperCase()} ${firstName}${date ? ' ' + date : ''}`; }; diff --git a/materials_ui/src/constants/categoryList.ts b/materials_ui/src/constants/categoryList.ts index e94f29ec..e366639a 100644 --- a/materials_ui/src/constants/categoryList.ts +++ b/materials_ui/src/constants/categoryList.ts @@ -1,11 +1,11 @@ export const materialsCategoryList = [ - 'Statement', - 'Exhibit', - 'MG Form', - 'Other Material', - 'Unused Material', - 'Defendant Pre Cons' -]; + { value: 'Statement', label: 'Statements' }, + { value: 'Exhibit', label: 'Exhibits' }, + { value: 'MG Form', label: 'MG forms' }, + { value: 'Other Material', label: 'Other material' }, + { value: 'Defendant Pre Cons', label: 'Defendant pre-cons' }, + { value: 'Unused Material', label: 'Unused material' }, +] as const; export const communicationsCategoryList = [ 'Bundle', @@ -13,7 +13,7 @@ export const communicationsCategoryList = [ 'Email', 'Item', 'Meeting', - 'Telephone call' + 'Telephone call', ]; export const communicationsWithList = [ @@ -23,7 +23,7 @@ export const communicationsWithList = [ 'Defence', 'Police', 'WCU', - 'Other' + 'Other', ]; export const typeList = [ @@ -36,5 +36,5 @@ export const typeList = [ 'Forwarded Internal Email', 'Correspondence', 'Other Communication', - 'Service of ABE' + 'Service of ABE', ]; diff --git a/materials_ui/src/constants/chargeStatus.ts b/materials_ui/src/constants/chargeStatus.ts index dca7e6d2..201b641c 100644 --- a/materials_ui/src/constants/chargeStatus.ts +++ b/materials_ui/src/constants/chargeStatus.ts @@ -1,9 +1,9 @@ export enum ChargeStatusCode { PreCharge = 1, - PostCharge = 2 + PostCharge = 2, } export const CHARGE_STATUS_SELECT_OPTIONS = [ { id: String(ChargeStatusCode.PreCharge), name: 'Pre-charge' }, - { id: String(ChargeStatusCode.PostCharge), name: 'Post-charge' } + { id: String(ChargeStatusCode.PostCharge), name: 'Post-charge' }, ]; diff --git a/materials_ui/src/constants/discard.ts b/materials_ui/src/constants/discard.ts index f7ddb643..3d42a892 100644 --- a/materials_ui/src/constants/discard.ts +++ b/materials_ui/src/constants/discard.ts @@ -4,5 +4,5 @@ export const DISCARD_MATERIAL_OPTIONS = [ { label: 'Duplicate', value: 'DUP' }, { label: 'Illegible', value: 'ILG' }, { label: 'Replaced', value: 'REP' }, - { label: 'Superfluous', value: 'SUP' } + { label: 'Superfluous', value: 'SUP' }, ]; diff --git a/materials_ui/src/constants/enum.ts b/materials_ui/src/constants/enum.ts index 7123bed8..2b994b8c 100644 --- a/materials_ui/src/constants/enum.ts +++ b/materials_ui/src/constants/enum.ts @@ -1,11 +1,11 @@ export enum PcdReviewCoreType { EarlyAdvice = 0, InitialReview = 1, - PreChargeDecisionAnalysis = 2 + PreChargeDecisionAnalysis = 2, } export const PcdReviewTypeLabel: Record = { [PcdReviewCoreType.EarlyAdvice]: 'Early Advice', [PcdReviewCoreType.InitialReview]: 'Initial Review', - [PcdReviewCoreType.PreChargeDecisionAnalysis]: 'Further Review' + [PcdReviewCoreType.PreChargeDecisionAnalysis]: 'Further Review', }; diff --git a/materials_ui/src/constants/featureFlagGroups.ts b/materials_ui/src/constants/featureFlagGroups.ts index bf5269ca..0bf22be7 100644 --- a/materials_ui/src/constants/featureFlagGroups.ts +++ b/materials_ui/src/constants/featureFlagGroups.ts @@ -3,5 +3,5 @@ export const PRIVATE_BETA_FEATURE_USER_GROUPS: Record = { 2: '1663cea9-062e-4f6e-a7ac-26f0942724f3', // group 2 3: '870b7ef9-5937-4eb6-9ea0-98158f522ed7', // group 3 (stale) 4: '4e8b4cd0-5794-4dd8-a5a9-3bf6c19efb17', // group 4 (stale) - 5: 'bc71fb89-46f2-4b85-8cfc-9aaea49e0e5a' // group 5 (stale) + 5: 'bc71fb89-46f2-4b85-8cfc-9aaea49e0e5a', // group 5 (stale) }; diff --git a/materials_ui/src/constants/query.ts b/materials_ui/src/constants/query.ts index 5ebcf983..9be4111c 100644 --- a/materials_ui/src/constants/query.ts +++ b/materials_ui/src/constants/query.ts @@ -26,7 +26,7 @@ export const QUERY_KEYS = { DOCUMENT_SEARCH: 'documentSearch', GET_ALL_DOCUMENTS: 'getAllDocuments', UPDATE_STATEMENT: 'updateStatement', - UPDATE_EXHIBIT: 'updateExhibit' + UPDATE_EXHIBIT: 'updateExhibit', }; export const DEFAULT_RESULTS_PER_PAGE = 20; diff --git a/materials_ui/src/constants/url.ts b/materials_ui/src/constants/url.ts index 2d025998..0faca3a0 100644 --- a/materials_ui/src/constants/url.ts +++ b/materials_ui/src/constants/url.ts @@ -15,7 +15,7 @@ export const URL = { COMMUNICATIONS: '/communications', ERROR: '/error', PCD_REVIEW: '/pcd-review', - PCD_REVIEW_DETAILS: '/pcd-review:pcdId' + PCD_REVIEW_DETAILS: '/pcd-review:pcdId', }; export const APP_DEFAULT_PAGE = URL.ROOT; @@ -50,8 +50,7 @@ export const API_ENDPOINTS = { RECLASSIFY: '/material/{materialId}/reclassify-complete', EXHIBIT_PRODUCERS: '/exhibit-producers', CREATE_WITNESS: '/case-witnesses', - CREATE_ACTION_PLAN: '/action-plan' + CREATE_ACTION_PLAN: '/action-plan', }; -export const AUTH_REDIRECT_URL = - '{apiUrl}/init?caseId={caseId}&screen={screenPath}'; +export const AUTH_REDIRECT_URL = '{apiUrl}/init?caseId={caseId}&screen={screenPath}'; diff --git a/materials_ui/src/context/AppContext.tsx b/materials_ui/src/context/AppContext.tsx index e3cba2cf..b7a7d0fb 100644 --- a/materials_ui/src/context/AppContext.tsx +++ b/materials_ui/src/context/AppContext.tsx @@ -16,7 +16,7 @@ export const AppContext = createContext({ wmReturnUrl: null, removeBanner: () => null, setBannerState: () => null, - setWmTriageUrl: () => null + setWmTriageUrl: () => null, }); export const AppContextProvider = ({ children }: PropsWithChildren) => { @@ -36,9 +36,7 @@ export const AppContextProvider = ({ children }: PropsWithChildren) => { }; const removeBanner = (bannerIdentifier: string) => { - setBanner( - banners.filter((banner) => banner.identifier !== bannerIdentifier) - ); + setBanner(banners.filter((banner) => banner.identifier !== bannerIdentifier)); }; const setWmTriageUrl = (url?: string | null) => { @@ -47,14 +45,7 @@ export const AppContextProvider = ({ children }: PropsWithChildren) => { return ( {children} diff --git a/materials_ui/src/context/FiltersContext/helpers/utils.ts b/materials_ui/src/context/FiltersContext/helpers/utils.ts index 4eed3107..e9ae0734 100644 --- a/materials_ui/src/context/FiltersContext/helpers/utils.ts +++ b/materials_ui/src/context/FiltersContext/helpers/utils.ts @@ -4,36 +4,32 @@ export const getDefaultState = (defaultFilters?: FilterItem): FilterItem => { return { filters: { ...(defaultFilters ? defaultFilters?.filters : {}) }, search: '', - sort: { ...(defaultFilters?.sort || { column: null, direction: null }) } + sort: { ...(defaultFilters?.sort || { column: null, direction: null }) }, }; }; // updates a FilterItem with new sort criteria -export const setSort = ( - column: string | null, - direction: SortBy -): FilterItem['sort'] => ({ column, direction }); +export const setSort = (column: string | null, direction: SortBy): FilterItem['sort'] => ({ + column, + direction, +}); // updates a FilterItem with new filter criteria export const setFilter = ( currentFilters: FilterItem['filters'], fieldGroup: string, name: string, - checked: boolean + checked: boolean, ): FilterItem['filters'] => { // if this filter hasn't been checked, create it if checked - if ( - !Object.prototype.hasOwnProperty.call(currentFilters, fieldGroup) && - checked - ) { + if (!Object.prototype.hasOwnProperty.call(currentFilters, fieldGroup) && checked) { return { ...currentFilters, [fieldGroup]: [name] }; } const newFilterValues = checked ? [...(currentFilters?.[fieldGroup] || []), name] - : currentFilters?.[fieldGroup]?.filter( - (existingFilterName) => existingFilterName !== name - ) || []; + : currentFilters?.[fieldGroup]?.filter((existingFilterName) => existingFilterName !== name) || + []; if (!newFilterValues?.length) { delete currentFilters?.[fieldGroup]; diff --git a/materials_ui/src/context/FiltersContext/index.tsx b/materials_ui/src/context/FiltersContext/index.tsx index 8c196e89..26a4a58b 100644 --- a/materials_ui/src/context/FiltersContext/index.tsx +++ b/materials_ui/src/context/FiltersContext/index.tsx @@ -1,10 +1,6 @@ import { createContext, PropsWithChildren, useState } from 'react'; -import type { - FilterItem, - FilterKeys, - FiltersContextState -} from './helpers/types'; +import type { FilterItem, FilterKeys, FiltersContextState } from './helpers/types'; import { getDefaultState } from './helpers/utils'; // DATA STRUCTURE @@ -18,14 +14,8 @@ import { getDefaultState } from './helpers/utils'; export type FiltersContext = { filters: FiltersContextState; - createFilterContext: ( - filterSet: FilterKeys, - defaultState?: FilterItem - ) => void; - updateFilterContext: ( - filterSet: FilterKeys, - newFilterSet: FilterItem - ) => void; + createFilterContext: (filterSet: FilterKeys, defaultState?: FilterItem) => void; + updateFilterContext: (filterSet: FilterKeys, newFilterSet: FilterItem) => void; resetFilterContext: (filterSet: FilterKeys) => void; resetAllFilters: () => void; }; @@ -41,34 +31,25 @@ export const FilterContext = createContext({ export const FilterProvider = ({ children }: PropsWithChildren) => { const [filters, setFilters] = useState({}); - const createFilterContext = ( - filterSet: string, - defaultState?: FilterItem - ) => { - setFilters((prev) => ({ - ...prev, - [filterSet]: getDefaultState(defaultState) - })); + const createFilterContext = (filterSet: string, defaultState?: FilterItem) => { + setFilters((prev) => ({ ...prev, [filterSet]: getDefaultState(defaultState) })); }; - const updateFilterContext = ( - filterSet: FilterKeys, - newFilterSet: FilterItem - ) => { + const updateFilterContext = (filterSet: FilterKeys, newFilterSet: FilterItem) => { setFilters((prev) => ({ ...prev, [filterSet]: { filters: { ...(newFilterSet.filters ?? {}) }, search: newFilterSet.search ?? '', - sort: newFilterSet.sort ?? prev[filterSet]?.sort - } + sort: newFilterSet.sort ?? prev[filterSet]?.sort, + }, })); }; const resetFilterContext = (filterSet: FilterKeys) => { setFilters((prev) => ({ ...prev, - [filterSet]: { sort: prev[filterSet]?.sort, filters: {}, search: '' } + [filterSet]: { sort: prev[filterSet]?.sort, filters: {}, search: '' }, })); }; @@ -81,7 +62,7 @@ export const FilterProvider = ({ children }: PropsWithChildren) => { resetAllFilters, createFilterContext, resetFilterContext, - updateFilterContext + updateFilterContext, }} > {children} diff --git a/materials_ui/src/context/GroupContext.tsx b/materials_ui/src/context/GroupContext.tsx index 852706cb..023c58a6 100644 --- a/materials_ui/src/context/GroupContext.tsx +++ b/materials_ui/src/context/GroupContext.tsx @@ -11,9 +11,7 @@ export type GroupContextType = { hasAppAccess: () => boolean; }; -export const GroupDataContext = createContext( - {} as GroupContextType -); +export const GroupDataContext = createContext({} as GroupContextType); export const GroupDataProvider = ({ children }: PropsWithChildren) => { const [groups, setGroups] = useState(() => { diff --git a/materials_ui/src/hooks/case-materials/useAutoReclassify.ts b/materials_ui/src/hooks/case-materials/useAutoReclassify.ts index 4ff4a5e8..d82fef14 100644 --- a/materials_ui/src/hooks/case-materials/useAutoReclassify.ts +++ b/materials_ui/src/hooks/case-materials/useAutoReclassify.ts @@ -17,7 +17,7 @@ export const useAutoReclassify = (options?: UseAutoReclassifyProps) => { const postAutoReclassify = () => request.post( - `urns/${caseInfo?.urn}/cases/${caseInfo?.id}/uma-reclassify` + `urns/${caseInfo?.urn}/cases/${caseInfo?.id}/uma-reclassify`, ); const { trigger, isMutating, error } = useSWRMutation( @@ -33,8 +33,8 @@ export const useAutoReclassify = (options?: UseAutoReclassifyProps) => { if (options?.onError) { options.onError(error); } - } - } + }, + }, ); return { isPending: isMutating, error, mutate: trigger }; diff --git a/materials_ui/src/hooks/case-materials/useBulkSetUnused.ts b/materials_ui/src/hooks/case-materials/useBulkSetUnused.ts index b8aaa3e2..2c0da72c 100644 --- a/materials_ui/src/hooks/case-materials/useBulkSetUnused.ts +++ b/materials_ui/src/hooks/case-materials/useBulkSetUnused.ts @@ -3,10 +3,7 @@ import useSWRMutation from 'swr/mutation'; import { useBanner, useLogger, useRequest } from '..'; import { QUERY_KEYS } from '../../constants/query'; import { SwrPayload } from '../../schemas'; -import { - BulkSetUnusedRequestType, - BulkSetUnusedResponseType -} from '../../schemas/bulkSetUnused'; +import { BulkSetUnusedRequestType, BulkSetUnusedResponseType } from '../../schemas/bulkSetUnused'; import { useCaseInfoStore } from '../../stores'; type UseBulkSetUnusedOptions = { @@ -14,10 +11,7 @@ type UseBulkSetUnusedOptions = { onSuccess?: (response: { data: BulkSetUnusedResponseType }) => void; }; -export const useBulkSetUnused = ({ - onError, - onSuccess -}: UseBulkSetUnusedOptions) => { +export const useBulkSetUnused = ({ onError, onSuccess }: UseBulkSetUnusedOptions) => { const request = useRequest(); const { resetBanner } = useBanner(); const { log } = useLogger(); @@ -25,14 +19,14 @@ export const useBulkSetUnused = ({ const postBulkSetUnused = async ( _url: string, - { arg: data }: SwrPayload + { arg: data }: SwrPayload, ) => { resetBanner(); return request .post( `urns/${caseInfo?.urn}/cases/${caseInfo?.id}/bulk-set-unused`, - data + data, ) .then((response) => response); }; @@ -49,11 +43,10 @@ export const useBulkSetUnused = ({ log({ logLevel: 3, - message: - 'HK-UI-FE: Failed to update status to Unused for selected materials.' + message: 'HK-UI-FE: Failed to update status to Unused for selected materials.', }); - } - } + }, + }, ); return { trigger, isMutating, error, data }; diff --git a/materials_ui/src/hooks/case-materials/useCaseMaterial.ts b/materials_ui/src/hooks/case-materials/useCaseMaterial.ts index cd613b21..d688a79f 100644 --- a/materials_ui/src/hooks/case-materials/useCaseMaterial.ts +++ b/materials_ui/src/hooks/case-materials/useCaseMaterial.ts @@ -4,9 +4,7 @@ import { SELECTED_MATERIAL_QUERY_PARAM } from '../../constants/materials'; export const useCaseMaterial = () => { const [searchParams, setSearchParams] = useSearchParams(); const newQueryCommands = new URLSearchParams(searchParams); - const selectedMaterialId = newQueryCommands.get( - SELECTED_MATERIAL_QUERY_PARAM - ); + const selectedMaterialId = newQueryCommands.get(SELECTED_MATERIAL_QUERY_PARAM); const selectMaterial = (materialId: number) => { newQueryCommands.set(SELECTED_MATERIAL_QUERY_PARAM, materialId.toString()); diff --git a/materials_ui/src/hooks/case-materials/useCaseMaterials.ts b/materials_ui/src/hooks/case-materials/useCaseMaterials.ts index a87b900f..d986c43b 100644 --- a/materials_ui/src/hooks/case-materials/useCaseMaterials.ts +++ b/materials_ui/src/hooks/case-materials/useCaseMaterials.ts @@ -15,39 +15,28 @@ export const useCaseMaterials = ({ dataType }: UseCaseMaterialsProps) => { const getCaseMaterials = async () => { const response = await request.get( - `/urns/${urn}/cases/${id}/case-materials` + `/urns/${urn}/cases/${id}/case-materials`, ); if (response.status === 422 || response.status !== 200) { - throw new Error( - `Validation error: Unable to process ${dataType} request` - ); + throw new Error(`Validation error: Unable to process ${dataType} request`); } return response.data; }; - const { data, error, isLoading, isValidating, mutate } = useSWR( - materialsKey, - getCaseMaterials, - { keepPreviousData: true } - ); + const { data, error, isLoading, isValidating, mutate } = useSWR(materialsKey, getCaseMaterials, { + keepPreviousData: true, + }); const filteredData = (data ?? []).filter((material) => dataType === 'communications' ? material.category === 'Communication' - : material.category !== 'Communication' + : material.category !== 'Communication', ); const isInitialLoading = !data && isLoading; const isRefreshing = !!data && isValidating; - return { - data, - loading: isInitialLoading, - refreshing: isRefreshing, - error, - filteredData, - mutate - }; + return { data, loading: isInitialLoading, refreshing: isRefreshing, error, filteredData, mutate }; }; diff --git a/materials_ui/src/hooks/case-materials/useDiscard.ts b/materials_ui/src/hooks/case-materials/useDiscard.ts index 6525fcc6..a1ecd1e6 100644 --- a/materials_ui/src/hooks/case-materials/useDiscard.ts +++ b/materials_ui/src/hooks/case-materials/useDiscard.ts @@ -7,29 +7,23 @@ import { SwrPayload } from '../../schemas'; import { CaseMaterialDiscardRequestType, CaseMaterialDiscardResponseType, - CaseMaterialsType + CaseMaterialsType, } from '../../schemas/caseMaterials'; -export type UseDiscardOptions = { - onError?: () => void; - onSuccess?: () => void; -}; +export type UseDiscardOptions = { onError?: () => void; onSuccess?: () => void }; -export const useDiscard = ( - material?: CaseMaterialsType, - options?: UseDiscardOptions -) => { +export const useDiscard = (material?: CaseMaterialsType, options?: UseDiscardOptions) => { const request = useRequest(); const { caseInfo } = useCaseInfoStore(); const { log } = useLogger(); const discardMaterialRequest = async ( _url: string, - { arg: data }: SwrPayload + { arg: data }: SwrPayload, ) => { return await request.patch( `urns/${caseInfo?.urn}/cases/${caseInfo?.id}/materials/${material?.materialId}/discard`, - data + data, ); }; @@ -46,7 +40,7 @@ export const useDiscard = ( log({ logLevel: 1, message: `HK-UI-FE: caseId ${caseInfo?.id} - Material [${material?.materialId}] discarded`, - errorMessage: '' + errorMessage: '', }); }, onError: (error: AxiosError) => { @@ -58,10 +52,10 @@ export const useDiscard = ( log({ logLevel: 3, message: `HK-UI-FE: Error discarding material ${material?.materialId}`, - errorMessage: `HK-UI-FE: Error discarding material ${material?.materialId}` + errorMessage: `HK-UI-FE: Error discarding material ${material?.materialId}`, }); - } - } + }, + }, ); return { isLoading: isMutating, trigger, error }; diff --git a/materials_ui/src/hooks/case-materials/useEditMaterial.tsx b/materials_ui/src/hooks/case-materials/useEditMaterial.tsx index cde189ca..3922f89e 100644 --- a/materials_ui/src/hooks/case-materials/useEditMaterial.tsx +++ b/materials_ui/src/hooks/case-materials/useEditMaterial.tsx @@ -8,7 +8,7 @@ import { EditExhibitType, EditStatementRequestType, EditStatementResponseType, - EditStatementType + EditStatementType, } from '../../schemas/forms/editStatement.ts'; import { SwrPayload } from '../../schemas/index.ts'; @@ -17,20 +17,17 @@ type UseEditMaterialOptions = { onSuccess?: (response: { materialId: number }) => void; }; -export const useEditMaterial = ({ - onError, - onSuccess - }: UseEditMaterialOptions) => { +export const useEditMaterial = ({ onError, onSuccess }: UseEditMaterialOptions) => { const request = useRequest(); const { caseInfo } = useCaseInfoStore(); const postUpdateStatement = async ( _url: string, - { arg: data }: SwrPayload + { arg: data }: SwrPayload, ): Promise => { const response = await request.patch( `urns/${caseInfo?.urn}/cases/${caseInfo?.id}/materials/${data?.materialId}/statement`, - data + data, ); return response.data; @@ -38,58 +35,54 @@ export const useEditMaterial = ({ const postUpdateExhibit = async ( _url: string, - { arg: data }: SwrPayload + { arg: data }: SwrPayload, ): Promise => { const response = await request.patch( `urns/${caseInfo?.urn}/cases/${caseInfo?.id}/materials/${data?.materialId}/exhibit`, { ...data, - newProducer: data?.existingproducerOrWitnessId - ? undefined - : data?.producedBy, + newProducer: data?.existingproducerOrWitnessId ? undefined : data?.producedBy, existingProducerOrWitnessId: !data?.existingproducerOrWitnessId ? undefined - : data?.existingproducerOrWitnessId - } + : data?.existingproducerOrWitnessId, + }, ); return response.data; }; - const { trigger: submitUpdateStatement, isMutating: isStatementUpdating } = - useSWRMutation( - caseInfo ? QUERY_KEYS.UPDATE_STATEMENT : null, - postUpdateStatement, - { - onError, - onSuccess: (response) => { - if (onSuccess) { - onSuccess({ materialId: response.updateStatement.id }); - } + const { trigger: submitUpdateStatement, isMutating: isStatementUpdating } = useSWRMutation( + caseInfo ? QUERY_KEYS.UPDATE_STATEMENT : null, + postUpdateStatement, + { + onError, + onSuccess: (response) => { + if (onSuccess) { + onSuccess({ materialId: response.updateStatement.id }); } - } - ); + }, + }, + ); - const { trigger: submitUpdateExhibit, isMutating: isExhibitUpdating } = - useSWRMutation( - caseInfo ? QUERY_KEYS.UPDATE_EXHIBIT : null, - postUpdateExhibit, - { - onError, - onSuccess: (response) => { - if (onSuccess) { - onSuccess({ materialId: response.updateExhibit.id }); - } + const { trigger: submitUpdateExhibit, isMutating: isExhibitUpdating } = useSWRMutation( + caseInfo ? QUERY_KEYS.UPDATE_EXHIBIT : null, + postUpdateExhibit, + { + onError, + onSuccess: (response) => { + if (onSuccess) { + onSuccess({ materialId: response.updateExhibit.id }); } - } - ); + }, + }, + ); const updateStatement = async (data: EditStatementType) => { await submitUpdateStatement({ ...data, statementDate: data?.hasStatementDate ? dayjs(data?.statementDate).format('YYYY-MM-DD') - : null + : null, }); }; @@ -97,9 +90,5 @@ export const useEditMaterial = ({ await submitUpdateExhibit(data); }; - return { - loading: isStatementUpdating || isExhibitUpdating, - updateExhibit, - updateStatement - }; + return { loading: isStatementUpdating || isExhibitUpdating, updateExhibit, updateStatement }; }; diff --git a/materials_ui/src/hooks/case-materials/useReadStatus.ts b/materials_ui/src/hooks/case-materials/useReadStatus.ts index f910d500..eaba9852 100644 --- a/materials_ui/src/hooks/case-materials/useReadStatus.ts +++ b/materials_ui/src/hooks/case-materials/useReadStatus.ts @@ -2,7 +2,7 @@ import { QUERY_KEYS } from '../../constants/query'; import { SwrPayload } from '../../schemas'; import { CaseMaterialReadStatusRequestType, - CaseMaterialReadStatusResponseType + CaseMaterialReadStatusResponseType, } from '../../schemas/caseMaterials'; import useSWRMutation from 'swr/mutation'; @@ -16,11 +16,11 @@ export const useReadStatus = () => { const updateReadStatus = async ( _url: string, - { arg: data }: SwrPayload + { arg: data }: SwrPayload, ) => { return await request.patch( `urns/${caseInfo?.urn}/cases/${caseInfo?.id}/materials/${data?.materialId}/read-status`, - data + data, ); }; @@ -31,7 +31,7 @@ export const useReadStatus = () => { onSuccess: () => { log({ logLevel: 1, - message: `HK-UI-FE: caseId ${caseInfo?.id} - Material [id] read status updated` + message: `HK-UI-FE: caseId ${caseInfo?.id} - Material [id] read status updated`, }); }, onError: (error) => { @@ -39,10 +39,10 @@ export const useReadStatus = () => { log({ logLevel: 3, message: 'HK-UI-FE: Error updating material read status', - errorMessage: 'HK-UI-FE: Error updating material read status' + errorMessage: 'HK-UI-FE: Error updating material read status', }); - } - } + }, + }, ); return { trigger, isMutating, error }; diff --git a/materials_ui/src/hooks/case-materials/useReclassify.ts b/materials_ui/src/hooks/case-materials/useReclassify.ts index bb9aa73f..67ac62ac 100644 --- a/materials_ui/src/hooks/case-materials/useReclassify.ts +++ b/materials_ui/src/hooks/case-materials/useReclassify.ts @@ -4,13 +4,13 @@ import { mapReclassifyExhibit, mapReclassifyMGForm, mapReclassifyOther, - mapReclassifyStatement + mapReclassifyStatement, } from '../../components/forms/Reclassify/mappers'; import { SwrPayload } from '../../schemas'; import { Reclassify_ClassificationEnumType, Reclassify_Orchestrated_Request_Type, - Reclassify_Orchestrated_Response_Type + Reclassify_Orchestrated_Response_Type, } from '../../schemas/forms/reclassify'; import { ReclassifyFormData } from './useReclassifyForm'; @@ -36,34 +36,28 @@ const getDataMapper = (classification: Reclassify_ClassificationEnumType) => { export type UseReclassifyOptions = { materialId: number; onError?: (error: Error) => void; - onSuccess?: (response: { - data: Reclassify_Orchestrated_Response_Type; - }) => void; + onSuccess?: (response: { data: Reclassify_Orchestrated_Response_Type }) => void; }; -export const useReclassify = ({ - materialId, - onError, - onSuccess -}: UseReclassifyOptions) => { +export const useReclassify = ({ materialId, onError, onSuccess }: UseReclassifyOptions) => { const request = useRequest(); const { caseInfo } = useCaseInfoStore(); const postReclassification = async ( _url: string, - { arg: data }: SwrPayload + { arg: data }: SwrPayload, ) => { return await request.post( `/urns/${caseInfo?.urn}/cases/${caseInfo?.id}/materials/${materialId}/reclassify-complete`, - data + data, ); }; - const { trigger: submitReclassify, isMutating: isReclassifyLoading } = - useSWRMutation(QUERY_KEYS.RECLASSIFY_MATERIAL, postReclassification, { - onError, - onSuccess - }); + const { trigger: submitReclassify, isMutating: isReclassifyLoading } = useSWRMutation( + QUERY_KEYS.RECLASSIFY_MATERIAL, + postReclassification, + { onError, onSuccess }, + ); const submitReclassification = async (data: ReclassifyFormData) => { const mapper = getDataMapper(data?.classification); diff --git a/materials_ui/src/hooks/case-materials/useReclassifyForm.ts b/materials_ui/src/hooks/case-materials/useReclassifyForm.ts index 72229a34..55b118fc 100644 --- a/materials_ui/src/hooks/case-materials/useReclassifyForm.ts +++ b/materials_ui/src/hooks/case-materials/useReclassifyForm.ts @@ -5,7 +5,7 @@ import { Reclassify_TypeMGFormType, Reclassify_TypeOtherType, Reclassify_TypeStatementType, - Reclassify_WitnessAndActionPlanType + Reclassify_WitnessAndActionPlanType, } from '../../schemas/forms/reclassify'; export type FormStep = 'classification' | 'summary' | 'addWitness' | 'subject'; @@ -15,10 +15,7 @@ type Reclassify_Statement_With_ActionPlan = { witnessActionPlan: Reclassify_WitnessAndActionPlanType; }; -type Reclassify_Statement_Without_ActionPlan = { - witnessId: number; - witnessActionPlan?: undefined; -}; +type Reclassify_Statement_Without_ActionPlan = { witnessId: number; witnessActionPlan?: undefined }; type Reclassify_Statement_Data = ( | Reclassify_Statement_With_ActionPlan @@ -37,7 +34,7 @@ export const useReclassifyForm = (material: CaseMaterialsType) => { const [formData, setFormData] = useState>({ materialId: material?.materialId, subject: material?.subject, - used: true + used: true, }); const changeFormStep = (step: FormStep) => { diff --git a/materials_ui/src/hooks/case-materials/useRename.ts b/materials_ui/src/hooks/case-materials/useRename.ts index 39e1a734..1ec0bf7c 100644 --- a/materials_ui/src/hooks/case-materials/useRename.ts +++ b/materials_ui/src/hooks/case-materials/useRename.ts @@ -1,34 +1,25 @@ import useSWRMutation from 'swr/mutation'; import { QUERY_KEYS } from '../../constants/query.ts'; import { TDocument } from '../../materials_components/DocumentSelectAccordion/getters/getDocumentList.tsx'; -import { - CaseMaterialRenameResponseType, - CaseMaterialsType -} from '../../schemas/caseMaterials.ts'; +import { CaseMaterialRenameResponseType, CaseMaterialsType } from '../../schemas/caseMaterials.ts'; import { SwrPayload } from '../../schemas/index.ts'; import { useCaseInfoStore } from '../../stores/index.ts'; import { useLogger, useRequest } from '../index.ts'; -type UseRenameOptions = { - onError?: (error: Error) => void; - onSuccess?: () => void; -}; +type UseRenameOptions = { onError?: (error: Error) => void; onSuccess?: () => void }; export const useRename = ( material: CaseMaterialsType | (TDocument & { materialId?: number }) | null, - options?: UseRenameOptions + options?: UseRenameOptions, ) => { const request = useRequest(); const { caseInfo } = useCaseInfoStore(); const { log } = useLogger(); - const renameMaterialRequest = ( - _url: string, - { arg: newSubject }: SwrPayload - ) => { + const renameMaterialRequest = (_url: string, { arg: newSubject }: SwrPayload) => { return request.patch( `/urns/${caseInfo?.urn}/cases/${caseInfo?.id}/materials/${material?.materialId}/rename`, - { materialId: material?.materialId, subject: newSubject } + { materialId: material?.materialId, subject: newSubject }, ); }; const { trigger, isMutating } = useSWRMutation( @@ -42,7 +33,7 @@ export const useRename = ( console.error('Error renaming material:', error); log({ logLevel: 1, - message: `HK-UI-FE: caseId [${caseInfo?.id}] - materialID [${material?.materialId}] has not been renamed.` + message: `HK-UI-FE: caseId [${caseInfo?.id}] - materialID [${material?.materialId}] has not been renamed.`, }); }, onSuccess: () => { @@ -51,10 +42,10 @@ export const useRename = ( log({ logLevel: 1, - message: `HK-UI-FE: caseId [${caseInfo?.id}] - materialID [${material?.materialId}] has been renamed.` + message: `HK-UI-FE: caseId [${caseInfo?.id}] - materialID [${material?.materialId}] has been renamed.`, }); - } - } + }, + }, ); return { trigger, isMutating }; diff --git a/materials_ui/src/hooks/case/useCaseDefendants.ts b/materials_ui/src/hooks/case/useCaseDefendants.ts index 84c04e51..c73bc6ec 100644 --- a/materials_ui/src/hooks/case/useCaseDefendants.ts +++ b/materials_ui/src/hooks/case/useCaseDefendants.ts @@ -3,10 +3,7 @@ import useSWR from 'swr'; import { useRequest } from '../'; import type { SelectOption } from '../../components/SelectList/SelectList'; import { QUERY_KEYS } from '../../constants/query'; -import { - DefendantsResponseType, - DefendantType -} from '../../schemas/defendants'; +import { DefendantsResponseType, DefendantType } from '../../schemas/defendants'; import { useCaseInfoStore } from '../../stores'; export const useCaseDefendants = () => { @@ -15,15 +12,14 @@ export const useCaseDefendants = () => { const getCaseDefendants = async () => await request - .get( - `/urns/${caseInfo?.urn}/cases/${caseInfo?.id}/case-defendants`, - { params: { caseId: caseInfo?.id } } - ) + .get(`/urns/${caseInfo?.urn}/cases/${caseInfo?.id}/case-defendants`, { + params: { caseId: caseInfo?.id }, + }) .then((response) => response.data); const { data: caseDefendants, isLoading } = useSWR( caseInfo?.id ? QUERY_KEYS.CASE_DEFENDANTS : null, - getCaseDefendants + getCaseDefendants, ); const formatDefendantName = (defendant?: DefendantType | null): string => { @@ -34,15 +30,13 @@ export const useCaseDefendants = () => { return `${defendant?.firstNames} ${defendant?.surname}`; }; - const getDefendantById = ( - defendantId?: number | string - ): DefendantType | null => { + const getDefendantById = (defendantId?: number | string): DefendantType | null => { if (!defendantId) { return null; } const defendant = caseDefendants?.defendants?.find( - (def) => def.id.toString() === defendantId?.toString() + (def) => def.id.toString() === defendantId?.toString(), ); return defendant || null; @@ -52,7 +46,7 @@ export const useCaseDefendants = () => { caseDefendants?.defendants?.map((defendant) => ({ id: defendant?.id, label: formatDefendantName(defendant) || '', - value: defendant?.id + value: defendant?.id, })) || []; return { @@ -60,6 +54,6 @@ export const useCaseDefendants = () => { loading: isLoading, getDefendantById, formatDefendantName, - selectOptions + selectOptions, }; }; diff --git a/materials_ui/src/hooks/case/useCaseInfo.ts b/materials_ui/src/hooks/case/useCaseInfo.ts index 6d9dc82b..a4215943 100644 --- a/materials_ui/src/hooks/case/useCaseInfo.ts +++ b/materials_ui/src/hooks/case/useCaseInfo.ts @@ -18,9 +18,5 @@ export const useCaseInfo = ({ caseId, urn }: UseCaseInfoProps) => { const { data, isLoading, isValidating, mutate } = useSWR(key, getCaseInfo); - return { - caseInfo: data || null, - loading: isLoading || isValidating, - refresh: mutate - }; + return { caseInfo: data || null, loading: isLoading || isValidating, refresh: mutate }; }; diff --git a/materials_ui/src/hooks/case/useCaseLockCheck.ts b/materials_ui/src/hooks/case/useCaseLockCheck.ts index 6dff97dc..835896af 100644 --- a/materials_ui/src/hooks/case/useCaseLockCheck.ts +++ b/materials_ui/src/hooks/case/useCaseLockCheck.ts @@ -17,7 +17,7 @@ export const useCaseLockCheck = (): UseCaseLockStatus => { const getCaseLockStatus = async () => { const response = await request.get( - `/urns/${caseInfo?.urn}/cases/${caseInfo?.id}/case-lock-info` + `/urns/${caseInfo?.urn}/cases/${caseInfo?.id}/case-lock-info`, ); return response.data; @@ -25,12 +25,12 @@ export const useCaseLockCheck = (): UseCaseLockStatus => { const { data, mutate: refreshCaseLockStatus } = useSWR( caseInfo ? QUERY_KEYS.CASE_LOCK_STATUS : null, - getCaseLockStatus + getCaseLockStatus, ); return { isLocked: data?.isLocked || false, name: data?.lockedByUser || null, - refreshCaseLockStatus + refreshCaseLockStatus, }; }; diff --git a/materials_ui/src/hooks/case/useCaseWitnesses.ts b/materials_ui/src/hooks/case/useCaseWitnesses.ts index 997e8282..93684a78 100644 --- a/materials_ui/src/hooks/case/useCaseWitnesses.ts +++ b/materials_ui/src/hooks/case/useCaseWitnesses.ts @@ -3,10 +3,7 @@ import useSWR from 'swr'; import { useRequest } from '../'; import type { SelectOption } from '../../components/SelectList/SelectList'; import { QUERY_KEYS } from '../../constants/query'; -import type { - WitnessListItemType, - WitnessListResponseType -} from '../../schemas/witness'; +import type { WitnessListItemType, WitnessListResponseType } from '../../schemas/witness'; import { useCaseInfoStore } from '../../stores'; export const useCaseWitnesses = () => { @@ -15,15 +12,14 @@ export const useCaseWitnesses = () => { const getCaseWitnesses = async () => await request - .get( - `urns/${caseInfo?.urn}/cases/${caseInfo?.id}/case-witnesses`, - { params: { caseId: caseInfo?.id } } - ) + .get(`urns/${caseInfo?.urn}/cases/${caseInfo?.id}/case-witnesses`, { + params: { caseId: caseInfo?.id }, + }) .then((response) => response.data); const { data: caseWitnesses, isLoading } = useSWR( caseInfo ? QUERY_KEYS.CASE_WITNESSES : null, - getCaseWitnesses + getCaseWitnesses, ); const formatWitnessName = (witness?: WitnessListItemType | null): string => { @@ -33,15 +29,13 @@ export const useCaseWitnesses = () => { return `${witness?.firstName} ${witness?.surname}`; }; - const getWitnessById = ( - witnessId?: number | string - ): WitnessListItemType | null => { + const getWitnessById = (witnessId?: number | string): WitnessListItemType | null => { if (!witnessId) { return null; } const witness = caseWitnesses?.witnesses?.find( - (witness) => witness.witnessId.toString() === witnessId?.toString() + (witness) => witness.witnessId.toString() === witnessId?.toString(), ); return witness || null; @@ -51,7 +45,7 @@ export const useCaseWitnesses = () => { caseWitnesses?.witnesses?.map((witness) => ({ id: witness?.witnessId, label: formatWitnessName(witness) || '', - value: witness?.witnessId + value: witness?.witnessId, })) || []; return { @@ -59,6 +53,6 @@ export const useCaseWitnesses = () => { loading: isLoading, data: caseWitnesses?.witnesses || [], selectOptions, - formatWitnessName + formatWitnessName, }; }; diff --git a/materials_ui/src/hooks/case/useWitnessStatements.ts b/materials_ui/src/hooks/case/useWitnessStatements.ts index 329817ab..16bc5586 100644 --- a/materials_ui/src/hooks/case/useWitnessStatements.ts +++ b/materials_ui/src/hooks/case/useWitnessStatements.ts @@ -3,10 +3,7 @@ import useSWR from 'swr'; import { useRequest } from '..'; import { QUERY_KEYS } from '../../constants/query'; -import type { - WitnessStatementResponseType, - WitnessStatementType -} from '../../schemas/witness'; +import type { WitnessStatementResponseType, WitnessStatementType } from '../../schemas/witness'; import { useCaseInfoStore } from '../../stores'; export const useWitnessStatements = () => { @@ -17,38 +14,28 @@ export const useWitnessStatements = () => { const getWitnessStatements = async () => await request .get( - `/urns/${caseInfo?.urn}/cases/${caseInfo?.id}/witnesses/${witnessId}/witness-statements` + `/urns/${caseInfo?.urn}/cases/${caseInfo?.id}/witnesses/${witnessId}/witness-statements`, ) .then((response) => response.data); const { data, isLoading } = useSWR( witnessId && caseInfo ? [QUERY_KEYS.WITNESS_STATEMENTS, witnessId] : null, - getWitnessStatements + getWitnessStatements, ); const setWitnessId = (id: number | null) => { setWitness(id); }; - const [witnessStatements, lastStatementId] = useMemo((): [ - WitnessStatementType[], - number - ] => { - const statements = (data?.statementsForWitness || []).sort( - (a, b) => a.title - b.title - ); + const [witnessStatements, lastStatementId] = useMemo((): [WitnessStatementType[], number] => { + const statements = (data?.statementsForWitness || []).sort((a, b) => a.title - b.title); const statementIds: number = Math.max( - ...(statements || []).map((item: WitnessStatementType) => item.title) + ...(statements || []).map((item: WitnessStatementType) => item.title), ); return [statements, statementIds]; }, [data]); - return { - data: witnessStatements, - loading: isLoading, - setWitnessId, - lastStatementId - }; + return { data: witnessStatements, loading: isLoading, setWitnessId, lastStatementId }; }; diff --git a/materials_ui/src/hooks/documents/useDocumentPdfUrl.ts b/materials_ui/src/hooks/documents/useDocumentPdfUrl.ts index d5912b62..5a57ed6e 100644 --- a/materials_ui/src/hooks/documents/useDocumentPdfUrl.ts +++ b/materials_ui/src/hooks/documents/useDocumentPdfUrl.ts @@ -12,7 +12,7 @@ const getDocumentBlobFromAxiosInstance = async (p: { try { const response = await p.axiosInstance.get( `/urns/${p.urn}/cases/${p.caseId}/materials/${p.materialId}/document`, - { responseType: 'blob' } + { responseType: 'blob' }, ); const blob = response.data; @@ -26,11 +26,7 @@ const getDocumentBlobFromAxiosInstance = async (p: { } }; -export const useDocumentPdfUrl = (p: { - urn: string; - caseId: number; - materialId: string; -}) => { +export const useDocumentPdfUrl = (p: { urn: string; caseId: number; materialId: string }) => { const [pdfUrl, setPdfUrl] = useState(); const axiosInstance = useAxiosInstance(); @@ -40,7 +36,7 @@ export const useDocumentPdfUrl = (p: { axiosInstance, urn: p.urn, caseId: p.caseId, - materialId: stripCmsPrefix(p.materialId) + materialId: stripCmsPrefix(p.materialId), }); if (!resp.success) return setPdfUrl(null); diff --git a/materials_ui/src/hooks/documents/useDocumentPreview.ts b/materials_ui/src/hooks/documents/useDocumentPreview.ts index e5c0f2d8..403461d5 100644 --- a/materials_ui/src/hooks/documents/useDocumentPreview.ts +++ b/materials_ui/src/hooks/documents/useDocumentPreview.ts @@ -14,13 +14,13 @@ export const useDocumentPreview = ({ materialId }: Props) => { await request .get( `/urns/${caseInfo?.urn}/cases/${caseInfo?.id}/materials/${materialId}/document`, - { responseType: 'blob' } + { responseType: 'blob' }, ) .then((response) => response.data); const { data, error, isLoading } = useSWR( caseInfo ? `${QUERY_KEYS.CASE_MATERIAL_FULL_DOCUMENT}/${materialId}` : null, - getDocumentPreview + getDocumentPreview, ); return { data, loading: isLoading, error }; diff --git a/materials_ui/src/hooks/documents/useDocumentTypes.ts b/materials_ui/src/hooks/documents/useDocumentTypes.ts index 57126653..0b8633ba 100644 --- a/materials_ui/src/hooks/documents/useDocumentTypes.ts +++ b/materials_ui/src/hooks/documents/useDocumentTypes.ts @@ -2,10 +2,7 @@ import useSWR from 'swr'; import { useRequest } from '../'; import type { SelectOption } from '../../components/SelectList/SelectList'; import { QUERY_KEYS } from '../../constants/query'; -import { - DocumentType, - DocumentTypeResponseType -} from '../../schemas/documentTypes'; +import { DocumentType, DocumentTypeResponseType } from '../../schemas/documentTypes'; import { useCaseInfoStore } from '../../stores'; export const useDocumentTypes = () => { @@ -14,35 +11,26 @@ export const useDocumentTypes = () => { const getDocumentTypes = async () => await request - .get( - `/urns/${caseInfo?.urn}/cases/${caseInfo?.id}/document-types` - ) + .get(`/urns/${caseInfo?.urn}/cases/${caseInfo?.id}/document-types`) .then((response) => response.data); const { data: documentTypes, isLoading } = useSWR( caseInfo ? QUERY_KEYS.DOCUMENT_TYPES : null, - getDocumentTypes + getDocumentTypes, ); - const getDocumentTypeById = ( - documentId: number | string - ): DocumentType | undefined => { - return documentTypes?.find( - (type) => type.id.toString() === documentId.toString() - ); + const getDocumentTypeById = (documentId: number | string): DocumentType | undefined => { + return documentTypes?.find((type) => type.id.toString() === documentId.toString()); }; // Step 1: Group by `group` - const types = documentTypes?.reduce>( - (acc, item) => { - if (!acc[item.group]) { - acc[item.group] = []; - } - acc[item.group]?.push(item); - return acc; - }, - {} - ); + const types = documentTypes?.reduce>((acc, item) => { + if (!acc[item.group]) { + acc[item.group] = []; + } + acc[item.group]?.push(item); + return acc; + }, {}); // Step 2: Sort each group by `name` for (const group in types) { @@ -54,11 +42,7 @@ export const useDocumentTypes = () => { return []; } - return types[group].map((type) => ({ - id: type.id, - label: type.name, - value: type.id - })); + return types[group].map((type) => ({ id: type.id, label: type.name, value: type.id })); }; return { getDocumentTypeById, loading: isLoading, selectOptions, types }; diff --git a/materials_ui/src/hooks/documents/useDocuments.ts b/materials_ui/src/hooks/documents/useDocuments.ts index c5689c13..f6de9cd8 100644 --- a/materials_ui/src/hooks/documents/useDocuments.ts +++ b/materials_ui/src/hooks/documents/useDocuments.ts @@ -17,16 +17,14 @@ export const useDocuments = () => { const { data, isLoading } = useSWR( urn && caseId ? [QUERY_KEYS.GET_ALL_DOCUMENTS, urn, caseId] : null, - getDocuments + getDocuments, ); const docTypes = data ? Array.from( new Set( - data - .map((doc) => doc.cmsDocType.documentType) - .filter((t) => t != null && t !== '') - ) + data.map((doc) => doc.cmsDocType.documentType).filter((t) => t != null && t !== ''), + ), ) : []; diff --git a/materials_ui/src/hooks/exhibits/useExhibitProducers.ts b/materials_ui/src/hooks/exhibits/useExhibitProducers.ts index dc6a924e..d600629e 100644 --- a/materials_ui/src/hooks/exhibits/useExhibitProducers.ts +++ b/materials_ui/src/hooks/exhibits/useExhibitProducers.ts @@ -3,10 +3,7 @@ import useSWR from 'swr'; import { useRequest } from '../'; import type { SelectOption } from '../../components/SelectList/SelectList'; import { QUERY_KEYS } from '../../constants/query'; -import { - ExhibitProducerResponseType, - ExhibitProducerType -} from '../../schemas/exhibitProducer'; +import { ExhibitProducerResponseType, ExhibitProducerType } from '../../schemas/exhibitProducer'; import { useCaseInfoStore } from '../../stores'; export const useExhibitProducers = () => { @@ -16,32 +13,28 @@ export const useExhibitProducers = () => { const getExhibitProducers = async () => await request .get( - `urns/${caseInfo?.urn}/cases/${caseInfo?.id}/case-exhibit-producers` + `urns/${caseInfo?.urn}/cases/${caseInfo?.id}/case-exhibit-producers`, ) .then((response) => response.data); const { data, isLoading, isValidating } = useSWR( caseInfo ? QUERY_KEYS.EXHIBIT_PRODUCERS : null, - getExhibitProducers + getExhibitProducers, ); const selectOptions: SelectOption[] = data?.exhibitProducers?.map((item) => ({ id: item?.id, label: item.producer, - value: item?.id + value: item?.id, })) || []; - const getExhibitProducerById = ( - id?: number | string - ): ExhibitProducerType | null => { + const getExhibitProducerById = (id?: number | string): ExhibitProducerType | null => { if (!id) { return null; } - const producer = data?.exhibitProducers?.find( - (item) => item.id.toString() === id?.toString() - ); + const producer = data?.exhibitProducers?.find((item) => item.id.toString() === id?.toString()); return producer || null; }; @@ -50,6 +43,6 @@ export const useExhibitProducers = () => { data: data?.exhibitProducers || [], getExhibitProducerById, loading: isLoading || isValidating, - selectOptions + selectOptions, }; }; diff --git a/materials_ui/src/hooks/exhibits/useExhibits.ts b/materials_ui/src/hooks/exhibits/useExhibits.ts index e8132cfd..8a86364a 100644 --- a/materials_ui/src/hooks/exhibits/useExhibits.ts +++ b/materials_ui/src/hooks/exhibits/useExhibits.ts @@ -4,14 +4,12 @@ export const useExhibits = () => { const { data: materials, loading: isLoading, - error + error, } = useCaseMaterials({ dataType: 'materials' }); const references = materials?.length ? materials - ?.map((material) => - (material.reference || '').trim().toLocaleLowerCase() - ) + ?.map((material) => (material.reference || '').trim().toLocaleLowerCase()) .filter((ref) => !!ref) : []; diff --git a/materials_ui/src/hooks/index.ts b/materials_ui/src/hooks/index.ts index 0cf39c4b..ce8f23cd 100644 --- a/materials_ui/src/hooks/index.ts +++ b/materials_ui/src/hooks/index.ts @@ -8,10 +8,7 @@ export { useEditMaterial } from './case-materials/useEditMaterial'; export { useReadStatus } from './case-materials/useReadStatus'; export { useReclassify } from './case-materials/useReclassify'; export { useReclassifyForm } from './case-materials/useReclassifyForm'; -export type { - FormStep, - ReclassifyFormData -} from './case-materials/useReclassifyForm'; +export type { FormStep, ReclassifyFormData } from './case-materials/useReclassifyForm'; export { useRename } from './case-materials/useRename'; export { useCaseDefendants } from './case/useCaseDefendants'; export { useCaseInfo } from './case/useCaseInfo'; @@ -26,10 +23,7 @@ export { useExhibits } from './exhibits/useExhibits'; export { usePCD } from './pcd-request/usePCD'; export { usePCDList } from './pcd-request/usePCDList'; export { useCaseSearch } from './search/useCaseSearch'; -export { - useDocumentSearch, - useDocumentSearchResults -} from './search/useDocumentSearch'; +export { useDocumentSearch, useDocumentSearchResults } from './search/useDocumentSearch'; export { useSearchTracker } from './search/useSearchTracker'; export { useAppRoute } from './ui/useAppRoute'; export { useBanner } from './ui/useBanner'; @@ -37,6 +31,7 @@ export { useDebounce } from './ui/useDebounce'; export { useFeatureFlag } from './ui/useFeatureFlag'; export { useFilters } from './ui/useFilters'; export { useLogger } from './ui/useLogger'; +export { usePageColors } from './ui/usePageColors'; export { usePager } from './ui/usePager'; export { useRequest } from './ui/useRequest'; export { useStoreLogger } from './ui/useStoreLogger'; diff --git a/materials_ui/src/hooks/pcd-request/usePCD.ts b/materials_ui/src/hooks/pcd-request/usePCD.ts index cfe090c3..60440d3b 100644 --- a/materials_ui/src/hooks/pcd-request/usePCD.ts +++ b/materials_ui/src/hooks/pcd-request/usePCD.ts @@ -13,13 +13,13 @@ export const usePCD = ({ pcdId }: UsePCDProps) => { const getPCDDetails = async () => await request .get( - `urns/${caseInfo?.urn}/cases/${caseInfo?.id}/pcds/${pcdId}/pcd-request` + `urns/${caseInfo?.urn}/cases/${caseInfo?.id}/pcds/${pcdId}/pcd-request`, ) .then((response) => response.data); const { data, error, isLoading, isValidating } = useSWR( caseInfo && pcdId ? `${QUERY_KEYS.PCD_REQUEST}/${pcdId}` : null, - getPCDDetails + getPCDDetails, ); return { data, error, isLoading: isLoading || isValidating }; diff --git a/materials_ui/src/hooks/pcd-request/usePCDList.ts b/materials_ui/src/hooks/pcd-request/usePCDList.ts index 9f564e92..4f2cfe1e 100644 --- a/materials_ui/src/hooks/pcd-request/usePCDList.ts +++ b/materials_ui/src/hooks/pcd-request/usePCDList.ts @@ -10,21 +10,17 @@ export const usePCDList = () => { const getPCDList = async () => await request .get( - `urns/${caseInfo?.urn}/cases/${caseInfo?.id}/pcds/${caseInfo?.id}/pcd-request-core` + `urns/${caseInfo?.urn}/cases/${caseInfo?.id}/pcds/${caseInfo?.id}/pcd-request-core`, ) .then((response) => response.data); const { data, error, isLoading, isValidating } = useSWR( caseInfo ? QUERY_KEYS.PCD_REQUESTS : null, - getPCDList + getPCDList, ); const sortByDate = (a: PCDListingType, b: PCDListingType) => Date.parse(b.decisionRequested) - Date.parse(a.decisionRequested); - return { - data: data?.sort(sortByDate) || undefined, - error, - isLoading: isLoading || isValidating - }; + return { data: data?.sort(sortByDate) || undefined, error, isLoading: isLoading || isValidating }; }; diff --git a/materials_ui/src/hooks/pcd-review/usePCDReviewCore.ts b/materials_ui/src/hooks/pcd-review/usePCDReviewCore.ts index aef00405..6af77aca 100644 --- a/materials_ui/src/hooks/pcd-review/usePCDReviewCore.ts +++ b/materials_ui/src/hooks/pcd-review/usePCDReviewCore.ts @@ -2,10 +2,7 @@ import useSWR from 'swr'; import { useCaseInfoStore, useRequest } from '..'; import { QUERY_KEYS } from '../../constants/query'; -import { - PCDReviewCoreResponseType, - PCDReviewCoreSchema -} from '../../schemas/pcdReview'; +import { PCDReviewCoreResponseType, PCDReviewCoreSchema } from '../../schemas/pcdReview'; export const usePCDReviewCore = () => { const request = useRequest(); @@ -14,7 +11,7 @@ export const usePCDReviewCore = () => { const getPCDReviewCore = async () => { try { const response = await request.get( - `urns/${caseInfo?.urn}/cases/${caseInfo?.id}/pcd-review-core` + `urns/${caseInfo?.urn}/cases/${caseInfo?.id}/pcd-review-core`, ); const parsedResponse = PCDReviewCoreSchema.safeParse(response.data); @@ -30,11 +27,10 @@ export const usePCDReviewCore = () => { } }; - const { data, error, isLoading, isValidating } = - useSWR( - caseInfo ? QUERY_KEYS.PCD_REVIEW_CORE : null, - getPCDReviewCore - ); + const { data, error, isLoading, isValidating } = useSWR( + caseInfo ? QUERY_KEYS.PCD_REVIEW_CORE : null, + getPCDReviewCore, + ); return { data, error, isLoading: isLoading || isValidating }; }; diff --git a/materials_ui/src/hooks/pcd-review/usePCDReviewDetails.ts b/materials_ui/src/hooks/pcd-review/usePCDReviewDetails.ts index e0449e99..45577eea 100644 --- a/materials_ui/src/hooks/pcd-review/usePCDReviewDetails.ts +++ b/materials_ui/src/hooks/pcd-review/usePCDReviewDetails.ts @@ -2,22 +2,16 @@ import useSWR from 'swr'; import { useCaseInfoStore, useRequest } from '..'; import { QUERY_KEYS } from '../../constants/query'; -import { - PCDReviewDetailsResponseType, - PCDReviewDetailsSchema -} from '../../schemas/pcdReview'; +import { PCDReviewDetailsResponseType, PCDReviewDetailsSchema } from '../../schemas/pcdReview'; export const usePCDReviewDetails = (historyId: number | undefined) => { const request = useRequest(); const { caseInfo } = useCaseInfoStore(); - const getPCDReviewDetails = async ([, requestedHistoryId]: readonly [ - string, - number - ]) => { + const getPCDReviewDetails = async ([, requestedHistoryId]: readonly [string, number]) => { try { const response = await request.get( - `urns/${caseInfo?.urn}/cases/${caseInfo?.id}/history/${requestedHistoryId}/pcd-review-details` + `urns/${caseInfo?.urn}/cases/${caseInfo?.id}/history/${requestedHistoryId}/pcd-review-details`, ); const parsedResponse = PCDReviewDetailsSchema.safeParse(response.data); @@ -32,13 +26,10 @@ export const usePCDReviewDetails = (historyId: number | undefined) => { } }; - const { data, error, isLoading, isValidating } = - useSWR( - caseInfo && historyId !== undefined - ? [QUERY_KEYS.PCD_REVIEW_REVIEW_DETAILS, historyId] - : null, - getPCDReviewDetails - ); + const { data, error, isLoading, isValidating } = useSWR( + caseInfo && historyId !== undefined ? [QUERY_KEYS.PCD_REVIEW_REVIEW_DETAILS, historyId] : null, + getPCDReviewDetails, + ); return { data, error, isLoading: isLoading || isValidating }; }; diff --git a/materials_ui/src/hooks/search/useCaseSearch.ts b/materials_ui/src/hooks/search/useCaseSearch.ts index 5b3a8786..48953ee6 100644 --- a/materials_ui/src/hooks/search/useCaseSearch.ts +++ b/materials_ui/src/hooks/search/useCaseSearch.ts @@ -27,25 +27,17 @@ export const useCaseSearch = (urn: string | undefined) => { setBanner({ type: 'error', header: 'Something went wrong', - content: - 'There was a problem with the server when searching for a case.' + content: 'There was a problem with the server when searching for a case.', }); } - } - } + }, + }, ); - return { - caseDetails: data ?? null, - loading: isLoading || isValidating, - refresh: mutate - }; + return { caseDetails: data ?? null, loading: isLoading || isValidating, refresh: mutate }; }; -export const getCaseDetails = async (p: { - axiosInstance: AxiosInstance; - urn: string; -}) => { +export const getCaseDetails = async (p: { axiosInstance: AxiosInstance; urn: string }) => { return p.axiosInstance.get(`/urns/${p.urn}/cases`); }; @@ -57,7 +49,7 @@ export const useCaseDetails = (p: { urn: string }) => { () => { if (!p.urn) return; return getCaseDetails({ axiosInstance, urn: p.urn }); - } + }, ); return rtn; diff --git a/materials_ui/src/hooks/search/useDocumentSearch.ts b/materials_ui/src/hooks/search/useDocumentSearch.ts index ac1d849b..2c6991d3 100644 --- a/materials_ui/src/hooks/search/useDocumentSearch.ts +++ b/materials_ui/src/hooks/search/useDocumentSearch.ts @@ -4,13 +4,10 @@ import { QUERY_KEYS } from '../../constants/query'; import { DocumentResultType, SearchResultType, - SearchTermResultType + SearchTermResultType, } from '../../schemas/documents'; -export const useDocumentSearch = ( - searchTerm: string | null, - trackerComplete: boolean -) => { +export const useDocumentSearch = (searchTerm: string | null, trackerComplete: boolean) => { const request = useRequest(); const { caseInfo } = useCaseInfoStore(); @@ -25,10 +22,8 @@ export const useDocumentSearch = ( .then((res) => res.data); const { data, isLoading } = useSWR( - searchTerm && trackerComplete - ? [QUERY_KEYS.DOCUMENT_SEARCH, urn, caseId, searchTerm] - : null, - getSearch + searchTerm && trackerComplete ? [QUERY_KEYS.DOCUMENT_SEARCH, urn, caseId, searchTerm] : null, + getSearch, ); return { searchResults: data ?? null, loading: isLoading }; @@ -36,19 +31,17 @@ export const useDocumentSearch = ( export const useDocumentSearchResults = ( documents: DocumentResultType = [], - searchResults: SearchResultType[] = [] + searchResults: SearchResultType[] = [], ) => { if (!documents || !searchResults) return []; const combinedSearchResults: SearchTermResultType[] = []; for (const doc of documents) { - const matches = searchResults.filter((sr) => sr.parentId === doc.parentId); + const matches = searchResults.filter((sr) => sr.documentId === doc.parentId); const resultsPerDocumentCount = matches.reduce((acc, curr) => { - const count = curr.words.filter((w) => - w.matchType?.includes('Exact') - ).length; + const count = curr.words.filter((w) => w.matchType === 'Exact').length; return acc + count; }, 0); @@ -70,9 +63,9 @@ export const useDocumentSearchResults = ( lineIndex: match.lineIndex, pageHeight: match.pageHeight, pageWidth: match.pageWidth, - words: match.words + words: match.words, })), - resultsPerDocumentCount + resultsPerDocumentCount, }); } } diff --git a/materials_ui/src/hooks/search/useSearchTracker.ts b/materials_ui/src/hooks/search/useSearchTracker.ts index 6816be2e..9764cbe7 100644 --- a/materials_ui/src/hooks/search/useSearchTracker.ts +++ b/materials_ui/src/hooks/search/useSearchTracker.ts @@ -15,11 +15,9 @@ export const useSearchTracker = (trigger: unknown) => { const getTracker = () => request.get(`/urns/${urn}/cases/${caseId}/tracker`); // Start pipeline once per case when first search is triggered - const { data: postData } = useSWR( - trigger ? ['tracker-init', urn, caseId] : null, - postInit, - { revalidateOnFocus: false } - ); + const { data: postData } = useSWR(trigger ? ['tracker-init', urn, caseId] : null, postInit, { + revalidateOnFocus: false, + }); // Poll until Completed const { data: trackerData, isLoading: trackerLoading } = useSWR( @@ -33,8 +31,8 @@ export const useSearchTracker = (trigger: unknown) => { ? 1000 : 0, dedupingInterval: 0, - revalidateOnFocus: false - } + revalidateOnFocus: false, + }, ); const failedToConvert = @@ -42,7 +40,7 @@ export const useSearchTracker = (trigger: unknown) => { (doc: SearchTermResultType) => doc.status === 'UnableToConvertToPdf' || doc.conversionStatus === 'UnexpectedError' || - doc.status === 'OcrAndIndexFailure' + doc.status === 'OcrAndIndexFailure', ) ?? []; const isComplete = trackerData?.data.status === 'Completed'; diff --git a/materials_ui/src/hooks/ui/navigateToViewDocumentPageInNewTab.ts b/materials_ui/src/hooks/ui/navigateToViewDocumentPageInNewTab.ts index 1fab1028..9dbec7a9 100644 --- a/materials_ui/src/hooks/ui/navigateToViewDocumentPageInNewTab.ts +++ b/materials_ui/src/hooks/ui/navigateToViewDocumentPageInNewTab.ts @@ -3,7 +3,5 @@ export const navigateToViewDocumentPageInNewTab = (p: { caseId: number; materialId: string | number; }) => { - window.open( - `${import.meta.env.BASE_URL}${p.urn}/${p.caseId}/view-document/${p.materialId}` - ); + window.open(`${import.meta.env.BASE_URL}${p.urn}/${p.caseId}/view-document/${p.materialId}`); }; diff --git a/materials_ui/src/hooks/ui/useAppRoute.ts b/materials_ui/src/hooks/ui/useAppRoute.ts index d1d89bbf..3abaf17b 100644 --- a/materials_ui/src/hooks/ui/useAppRoute.ts +++ b/materials_ui/src/hooks/ui/useAppRoute.ts @@ -15,7 +15,7 @@ export const APP_ROUTES = { SERVER_ERROR: 'service-down', UNAUTHORISED: 'unauthorized', CASE_SEARCH: 'case-search', - UPDATE_MATERIAL: 'update-material' + UPDATE_MATERIAL: 'update-material', } as const; type AppRouteKey = keyof typeof APP_ROUTES; diff --git a/materials_ui/src/hooks/ui/useBanner.ts b/materials_ui/src/hooks/ui/useBanner.ts index 34ca6154..3a2922cb 100644 --- a/materials_ui/src/hooks/ui/useBanner.ts +++ b/materials_ui/src/hooks/ui/useBanner.ts @@ -3,8 +3,7 @@ import { AppContext } from '../../context/AppContext'; import { BannerType } from '../../schemas'; export const useBanner = () => { - const { banners, clearBanners, removeBanner, setBannerState } = - useContext(AppContext); + const { banners, clearBanners, removeBanner, setBannerState } = useContext(AppContext); const setBanner = (banner: BannerType, persistBanners?: boolean) => { setBannerState(banner, persistBanners); diff --git a/materials_ui/src/hooks/ui/useFeatureFlag.ts b/materials_ui/src/hooks/ui/useFeatureFlag.ts index cf888ffc..cf073f24 100644 --- a/materials_ui/src/hooks/ui/useFeatureFlag.ts +++ b/materials_ui/src/hooks/ui/useFeatureFlag.ts @@ -3,13 +3,9 @@ import { PRIVATE_BETA_FEATURE_USER_GROUPS } from '../../constants'; import { GroupDataContext } from '../../context'; import { UserGroupType } from '../../schemas/user'; -type UseFeatureFlag = ( - allowedGroups: number[], - groupsOverride?: UserGroupType[] -) => boolean; +type UseFeatureFlag = (allowedGroups: number[], groupsOverride?: UserGroupType[]) => boolean; -const featureFlagsDisabled = - import.meta.env.VITE_DISABLE_FEATURE_FLAGS === 'true'; +const featureFlagsDisabled = import.meta.env.VITE_DISABLE_FEATURE_FLAGS === 'true'; export const useFeatureFlag = (): UseFeatureFlag => { const { groups = [] } = useContext(GroupDataContext); @@ -28,7 +24,7 @@ export const useFeatureFlag = (): UseFeatureFlag => { .map((groupId) => { return PRIVATE_BETA_FEATURE_USER_GROUPS[groupId]; }) - .includes(group?.id) + .includes(group?.id), ); }; }; diff --git a/materials_ui/src/hooks/ui/useFilters.ts b/materials_ui/src/hooks/ui/useFilters.ts index f8a23848..289d7226 100644 --- a/materials_ui/src/hooks/ui/useFilters.ts +++ b/materials_ui/src/hooks/ui/useFilters.ts @@ -1,46 +1,31 @@ import { useContext, useEffect, useState } from 'react'; import { FilterContext } from '../../context/FiltersContext'; -import type { - FilterItem, - FilterKeys -} from '../../context/FiltersContext/helpers/types'; +import type { FilterItem, FilterKeys } from '../../context/FiltersContext/helpers/types'; import { getDefaultState, setFilter as setFilterState, - setSort as setSortState + setSort as setSortState, } from '../../context/FiltersContext/helpers/utils'; -export const useFilters = ( - filterSetName: FilterKeys, - defaultState?: FilterItem -) => { - const { - createFilterContext, - filters, - resetFilterContext, - updateFilterContext - } = useContext(FilterContext); +export const useFilters = (filterSetName: FilterKeys, defaultState?: FilterItem) => { + const { createFilterContext, filters, resetFilterContext, updateFilterContext } = + useContext(FilterContext); const [shallowFilters, setShallowFilters] = useState( - () => filters[filterSetName] ?? getDefaultState(defaultState) + () => filters[filterSetName] ?? getDefaultState(defaultState), ); const setSort = (column: string | null) => { setShallowFilters((prev) => { const newSortDirection = - !prev.sort?.direction || prev.sort?.direction === 'descending' - ? 'ascending' - : 'descending'; + !prev.sort?.direction || prev.sort?.direction === 'descending' ? 'ascending' : 'descending'; const hasSortColumnChanged = prev.sort?.column !== column; const newSortState: FilterItem = { filters: filters[filterSetName]?.filters ?? {}, // if someone clicks on a new sort column, we want to change to ascending no matter what - sort: setSortState( - column, - hasSortColumnChanged ? 'ascending' : newSortDirection - ) + sort: setSortState(column, hasSortColumnChanged ? 'ascending' : newSortDirection), }; // we want to update context immediately for sorting @@ -52,18 +37,14 @@ export const useFilters = ( const setFilter = (filterGroup: string, name: string, value: boolean) => { setShallowFilters((prev) => ({ ...prev, - filters: setFilterState(prev.filters, filterGroup, name, value) + filters: setFilterState(prev.filters, filterGroup, name, value), })); }; - const setCheckboxFilter = ( - filterGroup: string, - name: string, - checked: boolean - ) => { + const setCheckboxFilter = (filterGroup: string, name: string, checked: boolean) => { setShallowFilters((prev) => ({ ...prev, - filters: setFilterState(prev.filters, filterGroup, name, checked) + filters: setFilterState(prev.filters, filterGroup, name, checked), })); }; @@ -76,11 +57,7 @@ export const useFilters = ( }; const resetFilters = () => { - setShallowFilters((prev) => ({ - ...prev, - filters: defaultState?.filters || {}, - search: '' - })); + setShallowFilters((prev) => ({ ...prev, filters: defaultState?.filters || {}, search: '' })); resetFilterContext(filterSetName); }; @@ -102,6 +79,6 @@ export const useFilters = ( setFilter, setCheckboxFilter, setSearch, - setSort + setSort, }; }; diff --git a/materials_ui/src/hooks/ui/useLogger.ts b/materials_ui/src/hooks/ui/useLogger.ts index e06c0c25..35106ff9 100644 --- a/materials_ui/src/hooks/ui/useLogger.ts +++ b/materials_ui/src/hooks/ui/useLogger.ts @@ -21,7 +21,7 @@ export const useLogger = () => { console.error('Failed to send log:', error); } }, - [request] + [request], ); return { log }; diff --git a/materials_ui/src/hooks/ui/usePageColors.ts b/materials_ui/src/hooks/ui/usePageColors.ts new file mode 100644 index 00000000..e8fd7d20 --- /dev/null +++ b/materials_ui/src/hooks/ui/usePageColors.ts @@ -0,0 +1,27 @@ +// used for setting high contrast colours to a pdf if user +// is in high contrast mode, otherwise returns undefined +// and falls back to usual behaviour + +import { useEffect, useState } from 'react'; + +const FORCED_COLORS_QUERY = '(forced-colors: active)'; +type PdfPageColors = { background: string; foreground: string }; + +const HIGH_CONTRAST_PAGE_COLORS: PdfPageColors = { background: 'Canvas', foreground: 'CanvasText' }; + +export function usePageColors(): PdfPageColors | undefined { + const [isForcedColors, setIsForcedColors] = useState(() => + Boolean(window.matchMedia?.(FORCED_COLORS_QUERY).matches), + ); + + useEffect(() => { + const mql = window.matchMedia?.(FORCED_COLORS_QUERY); + if (!mql) return; + + const onChange = (e: MediaQueryListEvent) => setIsForcedColors(e.matches); + mql.addEventListener('change', onChange); + return () => mql.removeEventListener('change', onChange); + }, []); + + return isForcedColors ? HIGH_CONTRAST_PAGE_COLORS : undefined; +} diff --git a/materials_ui/src/hooks/ui/usePager.ts b/materials_ui/src/hooks/ui/usePager.ts index c6205b42..bb450f50 100644 --- a/materials_ui/src/hooks/ui/usePager.ts +++ b/materials_ui/src/hooks/ui/usePager.ts @@ -1,12 +1,8 @@ -import { usePagination } from 'react-use-pagination'; -import { useSearchParams } from 'react-router-dom'; import { useEffect } from 'react'; +import { useSearchParams } from 'react-router-dom'; +import { usePagination } from 'react-use-pagination'; -type UsePagerOptions = { - totalItems?: number; - initialPage?: number; - initialPageSize?: number; -}; +type UsePagerOptions = { totalItems?: number; initialPage?: number; initialPageSize?: number }; export const usePager = (options?: UsePagerOptions) => { const usePaginationData = usePagination(options); @@ -17,10 +13,7 @@ export const usePager = (options?: UsePagerOptions) => { if (usePaginationData.currentPage === 0) { newQueryCommands.delete('page'); } else { - newQueryCommands.set( - 'page', - (usePaginationData.currentPage + 1 || '').toString() - ); + newQueryCommands.set('page', (usePaginationData.currentPage + 1 || '').toString()); } setQueryParams(newQueryCommands); }, [usePaginationData.currentPage]); diff --git a/materials_ui/src/hooks/ui/useRequest.ts b/materials_ui/src/hooks/ui/useRequest.ts index 7589b929..34be55fd 100644 --- a/materials_ui/src/hooks/ui/useRequest.ts +++ b/materials_ui/src/hooks/ui/useRequest.ts @@ -13,13 +13,13 @@ export const useRequest = () => { const axiosInstance = axios.create({ baseURL: `${POLARIS_GATEWAY_URL}/api/`, - withCredentials: true + withCredentials: true, }); axiosInstance.interceptors.request.use(async (config) => { const tokenResponse = await msalInstance.acquireTokenSilent({ ...loginRequest, - account: msalInstance.getActiveAccount()! + account: msalInstance.getActiveAccount()!, }); config.headers['Authorization'] = `Bearer ${tokenResponse.accessToken}`; @@ -47,16 +47,14 @@ export const useRequest = () => { if ( !error.request.responseURL.includes('/uma-reclassify') && - !/^\/api\/urns\/[^/]+\/cases$/.test( - new URL(error.request.responseURL).pathname - ) + !/^\/api\/urns\/[^/]+\/cases$/.test(new URL(error.request.responseURL).pathname) ) { return navigate(`/${getRoute('SERVER_ERROR', false)}`); } } return Promise.reject(error); - } + }, ); return axiosInstance; @@ -67,13 +65,13 @@ export const useAxiosInstance = () => { const axiosInstance = axios.create({ baseURL: `${POLARIS_GATEWAY_URL}/api/`, - withCredentials: true + withCredentials: true, }); axiosInstance.interceptors.request.use(async (config) => { const tokenResponse = await msalInstance.acquireTokenSilent({ ...loginRequest, - account: msalInstance.getActiveAccount()! + account: msalInstance.getActiveAccount()!, }); config.headers['Authorization'] = `Bearer ${tokenResponse.accessToken}`; diff --git a/materials_ui/src/hooks/ui/useTableActions.ts b/materials_ui/src/hooks/ui/useTableActions.ts index dd7ae84b..a213536c 100644 --- a/materials_ui/src/hooks/ui/useTableActions.ts +++ b/materials_ui/src/hooks/ui/useTableActions.ts @@ -23,7 +23,7 @@ export const useTableActions = ({ refreshData, setBanner, deselectItem, - resetBanner + resetBanner, }: TableActionsProps) => { const { getRoute } = useAppRoute(); const navigate = useNavigate(); @@ -32,37 +32,26 @@ export const useTableActions = ({ const [isReadStatusUpdating, setIsReadStatusUpdating] = useState(false); const handleReclassifyClick = () => { - navigate(getRoute('RECLASSIFICATION'), { - state: { row: selectedItems[0] } - }); + navigate(getRoute('RECLASSIFICATION'), { state: { row: selectedItems[0] } }); }; const handleDiscardClick = (returnToUrl: string) => { navigate(getRoute('DISCARD'), { - state: { selectedMaterial: selectedItems[0], returnTo: returnToUrl } + state: { selectedMaterial: selectedItems[0], returnTo: returnToUrl }, }); }; const handleEditClick = (material: CaseMaterialsType, returnTo: string) => { - navigate(getRoute('UPDATE_MATERIAL'), { - state: { returnTo, row: material } - }); + navigate(getRoute('UPDATE_MATERIAL'), { state: { returnTo, row: material } }); }; const handleRedactClick = (materialId: number) => { - navigate(getRoute('REVIEW_REDACT'), { - state: { materialId: `CMS-${materialId}` } - }); + navigate(getRoute('REVIEW_REDACT'), { state: { materialId: `CMS-${materialId}` } }); }; - const handleUnusedClick = ( - materials: CaseMaterialsType[], - returnTo: string - ) => { + const handleUnusedClick = (materials: CaseMaterialsType[], returnTo: string) => { resetBanner(); - navigate(getRoute('RECLASSIFY_TO_UNUSED'), { - state: { materials, returnTo } - }); + navigate(getRoute('RECLASSIFY_TO_UNUSED'), { state: { materials, returnTo } }); }; const determineReadStatusLabel = (items: CaseMaterialsType[]) => { @@ -83,7 +72,7 @@ export const useTableActions = ({ await trigger({ materialId: row.materialId, state: row.readStatus === READ_STATUS.READ ? 'unread' : 'read', - correspondenceId: uuidv4() + correspondenceId: uuidv4(), }); } @@ -93,15 +82,14 @@ export const useTableActions = ({ setBanner({ type: 'success', header: 'Read status updated', - content: 'Selected items have been updated.' + content: 'Selected items have been updated.', }); } catch (error) { console.error('Error updating read status:', error); setBanner({ type: 'error', header: 'Error updating read status', - content: - 'There was an error updating the read status of the selected items.' + content: 'There was an error updating the read status of the selected items.', }); } finally { setIsReadStatusUpdating(false); @@ -116,6 +104,6 @@ export const useTableActions = ({ handleUnusedClick, determineReadStatusLabel, handleReadStatusClick, - isReadStatusUpdating + isReadStatusUpdating, }; }; diff --git a/materials_ui/src/hooks/ui/useUserGroupsFeatureFlag.ts b/materials_ui/src/hooks/ui/useUserGroupsFeatureFlag.ts index 936cba76..94ce6554 100644 --- a/materials_ui/src/hooks/ui/useUserGroupsFeatureFlag.ts +++ b/materials_ui/src/hooks/ui/useUserGroupsFeatureFlag.ts @@ -3,8 +3,7 @@ import { PRIVATE_BETA_FEATURE_USER_GROUPS } from '../../constants'; export type UserGroupsFeatureFlags = { bulkRedaction: boolean }; -const featureFlagsDisabled = - import.meta.env.VITE_DISABLE_FEATURE_FLAGS === 'true'; +const featureFlagsDisabled = import.meta.env.VITE_DISABLE_FEATURE_FLAGS === 'true'; export const useUserGroupsFeatureFlag = (): UserGroupsFeatureFlags => { const { instance } = useMsal(); diff --git a/materials_ui/src/index.tsx b/materials_ui/src/index.tsx index a3d12cc1..fd856d2d 100644 --- a/materials_ui/src/index.tsx +++ b/materials_ui/src/index.tsx @@ -11,12 +11,15 @@ import './App.scss'; import { AppContextProvider } from './context/AppContext'; import { FilterProvider } from './context/FiltersContext'; import { msalConfig } from './msalInstance'; +import { initTelemetry } from './telemetry/appInsights'; + +initTelemetry(); if (import.meta.env.DEV && !import.meta.env.VITE_E2E) { const { worker } = await import('./mocks/browser'); await worker.start({ onUnhandledRequest: 'bypass', - serviceWorker: { url: `${import.meta.env.BASE_URL}mockServiceWorker.js` } + serviceWorker: { url: `${import.meta.env.BASE_URL}mockServiceWorker.js` }, }); } @@ -31,13 +34,7 @@ if (redirectResult?.account) { ReactDOM.createRoot(document.getElementById('root')!).render( - + - + , ); diff --git a/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/BulkRedactionForm.tsx b/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/BulkRedactionForm.tsx index ed9913b3..8e070601 100644 --- a/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/BulkRedactionForm.tsx +++ b/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/BulkRedactionForm.tsx @@ -1,8 +1,5 @@ import { useState } from 'react'; -import { - RedactionTypeSelect, - TRedactionType -} from '../PdfRedactor/RedactionTypeSelect'; +import { RedactionTypeSelect, TRedactionType } from '../PdfRedactor/RedactionTypeSelect'; import { GovUkButton } from '../PdfRedactor/templates/GovUkButton'; export type TBulkSearchSummary = @@ -23,10 +20,8 @@ export type TBulkProps = { const BulkSearchStatusText = (p: { search: TBulkSearchSummary }) => { if (p.search.status === 'idle') return null; if (p.search.status === 'loading') return Searching…; - if (p.search.status === 'error') - return Couldn’t search this document.; - if (p.search.count === 0) - return No other matches found in the document.; + if (p.search.status === 'error') return Couldn’t search this document.; + if (p.search.count === 0) return No other matches found in the document.; return ( This phrase appears {p.search.count} time @@ -41,8 +36,7 @@ export const BulkRedactionForm = (p: { }) => { const [redactionType, setRedactionType] = useState(); - const completedSearch = - p.bulkProps.search.status === 'done' ? p.bulkProps.search : undefined; + const completedSearch = p.bulkProps.search.status === 'done' ? p.bulkProps.search : undefined; const matchCount = completedSearch?.count ?? 0; const hasMatches = matchCount >= 1; const hasMultipleMatches = matchCount >= 2; @@ -58,11 +52,7 @@ export const BulkRedactionForm = (p: { return (
- + {p.bulkProps.search.status !== 'idle' && (
@@ -72,11 +62,7 @@ export const BulkRedactionForm = (p: { {!completedSearch && (
- + Redact this text - {p.bulkProps.search.status === 'error' - ? 'Try again' - : 'Find matching text'} + {p.bulkProps.search.status === 'error' ? 'Try again' : 'Find matching text'}
)} @@ -101,11 +85,7 @@ export const BulkRedactionForm = (p: { > View previous - + View next
@@ -113,11 +93,7 @@ export const BulkRedactionForm = (p: { {completedSearch && (
- + Redact this {hasMultipleMatches && ( diff --git a/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/CaseworkPdfRedactorWrapper.tsx b/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/CaseworkPdfRedactorWrapper.tsx index bf1b729a..138c7442 100644 --- a/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/CaseworkPdfRedactorWrapper.tsx +++ b/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/CaseworkPdfRedactorWrapper.tsx @@ -1,9 +1,11 @@ import { ComponentProps, useEffect, useState } from 'react'; import { Button } from '../../caseWorkApp/components/button'; +import { trackAction } from '../../telemetry/appInsights'; import { useAxiosInstance } from '../DocumentSelectAccordion/getters/getAxiosInstance'; import { TDocument } from '../DocumentSelectAccordion/getters/getDocumentList'; import { GovUkBanner } from '../DocumentSelectAccordion/templates/GovUkBanner'; import { categoriseDocument } from '../DocumentSelectAccordion/utils/categoriseDocument'; +import { TCategoryName } from '../DocumentSelectAccordion/utils/categoriseDocumentHelperUtils'; import { PdfRedactorCenteredModal } from '../PdfRedactor/modals/PdfRedactorCenteredModal'; import { PdfRedactorMiniModal } from '../PdfRedactor/modals/PdfRedactorMiniModal'; import { DeletionReasonForm } from '../PdfRedactor/PdfDeletionReasonForm'; @@ -14,59 +16,46 @@ import { GovUkButton } from '../PdfRedactor/templates/GovUkButton'; import { TCoord, TRedaction } from '../PdfRedactor/utils/coordUtils'; import { TIndexedDeletion } from '../PdfRedactor/utils/deletionUtils'; import { TMode } from '../PdfRedactor/utils/modeUtils'; +import { TIndexedRotation, TRotation } from '../PdfRedactor/utils/rotationUtils'; import { - TIndexedRotation, - TRotation -} from '../PdfRedactor/utils/rotationUtils'; -import type { - THighlightLayer, - TSearchHighlight + convertSearchHighlightToRedaction, + type THighlightLayer, + type TSearchHighlight, } from '../PdfRedactor/utils/searchHighlightUtils'; -import { - TTriggerData, - useTriggerListener -} from '../PdfRedactor/utils/useTriggger'; +import { TTriggerData, useTriggerListener } from '../PdfRedactor/utils/useTriggger'; import { useWindowMouseListener } from '../PdfRedactor/utils/useWindowMouseListener'; import { useBulkRedactionFlow } from './hooks/useBulkRedactionFlow'; import { useDocumentCheckOutRequest } from './hooks/useDocumentCheckOutRequest'; import { RedactionPopover } from './RedactionPopover'; import { combineDeletionsWithDeletionDetails, - TDeletionDetail + TDeletionDetail, } from './utils/combineRedactionsDeletions'; import { saveDeletions } from './utils/saveDeletionsUtils'; import { saveRedactions } from './utils/saveRedactionsUtils'; import { saveRotations } from './utils/saveRotationsUtils'; -const presentationWriteFlagToRedactionDisabledMessageMap: { - [k: string]: string; -} = { - IsRedactionServiceOffline: - 'Redaction is currently unavailable and undergoing maintenance.', +const presentationWriteFlagToRedactionDisabledMessageMap = { + IsRedactionServiceOffline: 'Redaction is currently unavailable and undergoing maintenance.', OnlyAvailableInCms: 'This document can only be redacted in CMS.', - DocTypeNotAllowed: 'Redaction is not supported for this document type.', + DocTypeNotAllowed: 'You cannot redact this type of document', OriginalFileTypeNotAllowed: 'Redaction is not supported for this file type.', - IsDispatched: 'This is a dispatched document.', + IsDispatched: 'You cannot redact this type of document because it has been dispatched.', IsPageRotationModeOn: - 'Redaction is unavailable in page rotation mode, please turn off page rotation to continue with redaction.' -}; -const getDocumentRedactionDisabledMessage = ( - doc: TDocument | null | undefined -) => { + 'Redaction is unavailable in page rotation mode, please turn off page rotation to continue with redaction.', +} as const; + +const getDocumentRedactionDisabledMessage = (doc: TDocument | null | undefined) => { const writePresentationFlag = doc?.presentationFlags?.write; if (!writePresentationFlag) return null; - const value = - presentationWriteFlagToRedactionDisabledMessageMap[ - `${writePresentationFlag}` - ]; + const value = (presentationWriteFlagToRedactionDisabledMessageMap as { [k: string]: string })[ + `${writePresentationFlag}` + ]; return value ? value : null; }; -const createCheckoutMessageFromCheckoutResponse = (p: { - action?: string; - message?: string; -}) => +const createCheckoutMessageFromCheckoutResponse = (p: { action?: string; message?: string }) => p.message ? `It is not possible to ${p.action} as ${p.message}. Please try again later.` : 'Something has gone wrong, please try again later'; @@ -95,26 +84,18 @@ export const CaseworkPdfRedactorWrapper = (p: { checkInDocumentTriggerData: TTriggerData; }) => { const [isDocumentCheckedOut, setIsDocumentCheckedOut] = useState(false); - const [selectedRedactionTypes, setSelectedRedactionTypes] = useState< - TRedactionType[] - >([]); + const [selectedRedactionTypes, setSelectedRedactionTypes] = useState([]); - const documentCheckOutRequest = useDocumentCheckOutRequest({ - caseId: p.caseId, - urn: p.urn - }); + const documentCheckOutRequest = useDocumentCheckOutRequest({ caseId: p.caseId, urn: p.urn }); const checkInDocument = async () => { if (!isDocumentCheckedOut) return; const resp = await documentCheckOutRequest.checkIn({ parentId: p.parentId, - childId: p.childId + childId: p.childId, }); if (resp.success) setIsDocumentCheckedOut(false); }; - useTriggerListener({ - triggerData: p.checkInDocumentTriggerData, - fn: () => checkInDocument() - }); + useTriggerListener({ triggerData: p.checkInDocumentTriggerData, fn: () => checkInDocument() }); useEffect(() => { return () => { @@ -128,22 +109,17 @@ export const CaseworkPdfRedactorWrapper = (p: { const [deletionDetails, setDeletionDetails] = useState([]); - const documentCategory = p.document - ? categoriseDocument(p.document) - : undefined; - const isUnredactableDocumentCategory = ['review', 'defendant'].includes( - documentCategory ?? '' - ); - const isDocumentDispatched = - p.document?.presentationFlags?.write === 'IsDispatched'; + const documentCategory = p.document ? categoriseDocument(p.document) : undefined; + const isUnredactableDocumentCategory = + documentCategory && + (['review', 'defendantPreCons'] as TCategoryName[]).includes(documentCategory); + const isDocumentDispatched = p.document?.presentationFlags?.write === 'IsDispatched'; const cleanupDeletionDetails = () => { const deletionIds = Object.values(indexedDeletion) .filter((del) => del.isDeleted) .map((del) => del.id); - setDeletionDetails((prev) => - prev.filter((detail) => deletionIds.includes(detail.deletionId)) - ); + setDeletionDetails((prev) => prev.filter((detail) => deletionIds.includes(detail.deletionId))); }; useEffect(() => cleanupDeletionDetails(), [indexedDeletion]); @@ -162,25 +138,18 @@ export const CaseworkPdfRedactorWrapper = (p: { childId: p.childId, parentId: p.parentId, setRedactions, - setSelectedRedactionTypes + setSelectedRedactionTypes, }); - const searchLayer: THighlightLayer = { - highlights: p.searchHighlights ?? [], - focusedId: - p.focusedSearchIndex !== undefined - ? p.searchHighlights?.[p.focusedSearchIndex]?.id - : undefined - }; - - const [documentIsCheckedOutPopupProps, setDocumentIsCheckedOutPopupProps] = - useState<{ action: string; message: string } | null>(null); - const [ - documentIsUnableToBeRedactedPopupProps, - setDocumentIsUnableToBeRedactedPopupProps - ] = useState<{ message: string } | null>(null); - const [redactionDisabledModalProps, setRedactionDisabledModalProps] = + const [documentIsCheckedOutPopupProps, setDocumentIsCheckedOutPopupProps] = useState<{ + action: string; + message: string; + } | null>(null); + const [documentIsUnableToBeRedactedPopupProps, setDocumentIsUnableToBeRedactedPopupProps] = useState<{ message: string } | null>(null); + const [redactionDisabledModalProps, setRedactionDisabledModalProps] = useState<{ + message: string; + } | null>(null); const [deleteReasonPopupProps, setDeleteReasonPopupProps] = useState & TCoord, @@ -197,11 +166,7 @@ export const CaseworkPdfRedactorWrapper = (p: { }; const unrotatePage = (pageNumber: number) => { setIndexedRotation((prev) => { - const noRotation: TRotation = { - id: crypto.randomUUID(), - pageNumber, - rotationDegrees: 0 - }; + const noRotation: TRotation = { id: crypto.randomUUID(), pageNumber, rotationDegrees: 0 }; return { ...prev, [pageNumber]: noRotation }; }); }; @@ -210,12 +175,79 @@ export const CaseworkPdfRedactorWrapper = (p: { if (isDocumentCheckedOut) return { success: true } as const; const checkoutResponse = await documentCheckOutRequest.checkOut({ parentId: p.parentId, - childId: p.childId + childId: p.childId, }); setIsDocumentCheckedOut(checkoutResponse.success); return checkoutResponse; }; + const handleAddRedactions = async ({ + redactions: add, + highlightedText, + }: { + redactions: TRedaction[]; + highlightedText: string | undefined; + }) => { + if (isUnredactableDocumentCategory || isDocumentDispatched) { + removeRedactions(add.map((x) => x.id)); + const message = (() => { + if (isDocumentDispatched) + return presentationWriteFlagToRedactionDisabledMessageMap.IsDispatched; + return presentationWriteFlagToRedactionDisabledMessageMap.DocTypeNotAllowed; + })(); + setDocumentIsUnableToBeRedactedPopupProps({ message }); + return; + } + + const popoverAnchor = (() => { + if (highlightedText) { + const rect = window.getSelection()?.getRangeAt(0)?.getBoundingClientRect(); + if (rect && rect.width > 0) { + return { x: (rect.left + rect.right) / 2, y: rect.top }; + } + } + return { x: mousePos.current.x, y: mousePos.current.y }; + })(); + + const checkoutResponsePromise = checkCheckoutStatus(); + const redactionDisabledMessage = getDocumentRedactionDisabledMessage(p.document); + if (redactionDisabledMessage) { + removeRedactions(add.map((x) => x.id)); + setRedactionDisabledModalProps({ message: redactionDisabledMessage }); + return; + } + const checkoutResponse = await checkoutResponsePromise; + if (!checkoutResponse.success) { + removeRedactions(add.map((x) => x.id)); + const message = createCheckoutMessageFromCheckoutResponse({ + action: 'redact', + message: checkoutResponse.message, + }); + setDocumentIsCheckedOutPopupProps({ action: 'redact', message }); + return; + } + + bulkFlow.openPopover({ + x: popoverAnchor.x, + y: popoverAnchor.y, + redactionIds: add.map((x) => x.id), + highlightedText, + }); + }; + + const searchLayer: THighlightLayer = { + highlights: p.searchHighlights ?? [], + focusedId: + p.focusedSearchIndex !== undefined + ? p.searchHighlights?.[p.focusedSearchIndex]?.id + : undefined, + onHighlightClick: (highlight) => { + const redaction = convertSearchHighlightToRedaction(highlight); + setRedactions((prev) => [...prev, redaction]); + handleAddRedactions({ redactions: [redaction], highlightedText: undefined }); + }, + }; + return (
{redactionDisabledModalProps && @@ -226,7 +258,7 @@ export const CaseworkPdfRedactorWrapper = (p: { } - contentHeading="Unable to redact" + contentHeading="Unable to redact document" contentBody={redactionDisabledModalProps.message} /> @@ -267,8 +299,7 @@ export const CaseworkPdfRedactorWrapper = (p: { })()} {documentIsUnableToBeRedactedPopupProps && (() => { - const closeModal = () => - setDocumentIsUnableToBeRedactedPopupProps(null); + const closeModal = () => setDocumentIsUnableToBeRedactedPopupProps(null); return ( -
-

Unable to redact document

-
{documentIsUnableToBeRedactedPopupProps.message}
-
-
- Ok -
-
+ + + + } + contentHeading="Unable to redact document" + contentBody={documentIsUnableToBeRedactedPopupProps.message} + />
); })()} {bulkFlow.popupProps && ( - + )} {deleteReasonPopupProps && (() => { @@ -335,59 +366,7 @@ export const CaseworkPdfRedactorWrapper = (p: { setSelectedRedactionTypes([]); } }} - onAddRedactions={async ({ redactions: add, highlightedText }) => { - if (isUnredactableDocumentCategory || isDocumentDispatched) { - removeRedactions(add.map((x) => x.id)); - const message = (() => { - if (isDocumentDispatched) return 'This is a dispatched document'; - return 'Redaction is not supported for this document type.'; - })(); - setDocumentIsUnableToBeRedactedPopupProps({ message }); - return; - } - - const popoverAnchor = (() => { - if (highlightedText) { - const rect = window - .getSelection() - ?.getRangeAt(0) - ?.getBoundingClientRect(); - if (rect && rect.width > 0) { - return { x: (rect.left + rect.right) / 2, y: rect.top }; - } - } - return { x: mousePos.current.x, y: mousePos.current.y }; - })(); - - const checkoutResponsePromise = checkCheckoutStatus(); - const redactionDisabledMessage = getDocumentRedactionDisabledMessage( - p.document - ); - if (redactionDisabledMessage) { - removeRedactions(add.map((x) => x.id)); - setRedactionDisabledModalProps({ - message: redactionDisabledMessage - }); - return; - } - const checkoutResponse = await checkoutResponsePromise; - if (!checkoutResponse.success) { - removeRedactions(add.map((x) => x.id)); - const message = createCheckoutMessageFromCheckoutResponse({ - action: 'redact', - message: checkoutResponse.message - }); - setDocumentIsCheckedOutPopupProps({ action: 'redact', message }); - return; - } - - bulkFlow.openPopover({ - x: popoverAnchor.x, - y: popoverAnchor.y, - redactionIds: add.map((x) => x.id), - highlightedText - }); - }} + onAddRedactions={handleAddRedactions} onRemoveRedactions={() => {}} onSaveRedactions={async () => { p.onRedactionSaveStatusChange('saving'); @@ -398,15 +377,13 @@ export const CaseworkPdfRedactorWrapper = (p: { caseId: p.caseId, childId: p.childId, parentId: p.parentId, - redactions + redactions, }); setRedactions([]); + trackAction('Redacted', { materialId: p.parentId }); p.onRedactionSaveStatusChange('saved'); if (p.document) p.onModification(p.document); - await documentCheckOutRequest.checkIn({ - parentId: p.parentId, - childId: p.childId - }); + await documentCheckOutRequest.checkIn({ parentId: p.parentId, childId: p.childId }); } catch (error) { console.error('Failed to save redactions:', error); setRedactions([]); @@ -415,11 +392,7 @@ export const CaseworkPdfRedactorWrapper = (p: { } }} onShowRedactionLogModal={(redactions) => { - p.onShowRedactionLogModal({ - mode: 'list', - redactions, - selectedRedactionTypes - }); + p.onShowRedactionLogModal({ mode: 'list', redactions, selectedRedactionTypes }); }} indexedRotation={indexedRotation} onRotationsChange={(newRotations) => setIndexedRotation(newRotations)} @@ -432,7 +405,7 @@ export const CaseworkPdfRedactorWrapper = (p: { const message = createCheckoutMessageFromCheckoutResponse({ action: 'rotate', - message: checkoutResponse.message + message: checkoutResponse.message, }); setDocumentIsCheckedOutPopupProps({ action: 'rotate', message }); }} @@ -447,7 +420,7 @@ export const CaseworkPdfRedactorWrapper = (p: { const message = createCheckoutMessageFromCheckoutResponse({ action: 'delete', - message: checkoutResponse.message + message: checkoutResponse.message, }); setDocumentIsCheckedOutPopupProps({ action: 'delete', message }); return; @@ -455,7 +428,7 @@ export const CaseworkPdfRedactorWrapper = (p: { const newDeletionDetails = { deletionId: add.id, - randomId: `This deletion does ${crypto.randomUUID()}` + randomId: `This deletion does ${crypto.randomUUID()}`, }; setDeletionDetails((prev) => [...prev, newDeletionDetails]); setDeleteReasonPopupProps(() => ({ @@ -464,14 +437,14 @@ export const CaseworkPdfRedactorWrapper = (p: { pageNumber: add.pageNumber, documentId: 'This document does not exist', urn: 'This URN does not exist', - caseId: 'This case does not exist' + caseId: 'This case does not exist', })); }} onDeletionRemove={() => {}} onSaveDeletions={async () => { combineDeletionsWithDeletionDetails({ deletions: Object.values(indexedDeletion), - deletionDetails + deletionDetails, }); await saveDeletions({ @@ -480,13 +453,10 @@ export const CaseworkPdfRedactorWrapper = (p: { caseId: p.caseId, childId: p.childId, parentId: p.parentId, - deletions: Object.values(indexedDeletion) + deletions: Object.values(indexedDeletion), }); if (p.document) p.onModification(p.document); - await documentCheckOutRequest.checkIn({ - parentId: p.parentId, - childId: p.childId - }); + await documentCheckOutRequest.checkIn({ parentId: p.parentId, childId: p.childId }); }} onSaveRotations={async () => { const checkoutResponse = await checkCheckoutStatus(); @@ -494,7 +464,7 @@ export const CaseworkPdfRedactorWrapper = (p: { if (!checkoutResponse.success) { const message = createCheckoutMessageFromCheckoutResponse({ action: 'rotate', - message: checkoutResponse.message + message: checkoutResponse.message, }); setDocumentIsCheckedOutPopupProps({ action: 'rotate', message }); @@ -506,13 +476,10 @@ export const CaseworkPdfRedactorWrapper = (p: { caseId: p.caseId, childId: p.childId, parentId: p.parentId, - rotations: Object.values(indexedRotation) + rotations: Object.values(indexedRotation), }); if (p.document) p.onModification(p.document); - await documentCheckOutRequest.checkIn({ - parentId: p.parentId, - childId: p.childId - }); + await documentCheckOutRequest.checkIn({ parentId: p.parentId, childId: p.childId }); }} initRedactions={p.initRedactions} onNumOfDocPagesChanged={p.onNumOfPagesDocumentChange} diff --git a/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/hooks/useBulkRedactionFlow.ts b/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/hooks/useBulkRedactionFlow.ts index d8923f53..4d9cb658 100644 --- a/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/hooks/useBulkRedactionFlow.ts +++ b/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/hooks/useBulkRedactionFlow.ts @@ -20,9 +20,7 @@ export const useBulkRedactionFlow = (p: { }) => { const featureFlags = useUserGroupsFeatureFlag(); - const [popupProps, setPopupProps] = useState( - null - ); + const [popupProps, setPopupProps] = useState(null); const removeRedactions = (ids: string[]) => p.setRedactions((prev) => prev.filter((r) => !ids.includes(r.id))); @@ -32,7 +30,7 @@ export const useBulkRedactionFlow = (p: { urn: p.urn, caseId: p.caseId, versionId: p.childId, - documentId: p.parentId + documentId: p.parentId, }); // pin the popover above whichever match is currently focused @@ -40,14 +38,10 @@ export const useBulkRedactionFlow = (p: { const focusedId = bulkSearch.focusedCandidate?.id; if (!focusedId) return; - const elm = document.querySelector( - `[data-text-highlight-id="${focusedId}"]` - ); + const elm = document.querySelector(`[data-text-highlight-id="${focusedId}"]`); if (!elm) return; const matchTop = elm.getBoundingClientRect().top; - setPopupProps((prev) => - prev ? { ...prev, x: window.innerWidth / 2, y: matchTop } : prev - ); + setPopupProps((prev) => (prev ? { ...prev, x: window.innerWidth / 2, y: matchTop } : prev)); }, [bulkSearch.focusedCandidate?.id]); const trimmedSearchText = popupProps?.highlightedText?.trim() ?? ''; @@ -65,7 +59,7 @@ export const useBulkRedactionFlow = (p: { const onSaveSingle = (currentType: TRedactionType) => { p.setSelectedRedactionTypes((prev) => [ ...prev, - { id: currentType.id, name: currentType.name } + { id: currentType.id, name: currentType.name }, ]); closePopover(); }; @@ -85,7 +79,7 @@ export const useBulkRedactionFlow = (p: { p.setRedactions((prev) => [...prev, focused]); p.setSelectedRedactionTypes((prev) => [ ...prev, - { id: currentType.id, name: currentType.name } + { id: currentType.id, name: currentType.name }, ]); const willBeEmpty = bulkSearch.candidates.length === 1; bulkSearch.removeFocused(); @@ -97,10 +91,7 @@ export const useBulkRedactionFlow = (p: { p.setRedactions((prev) => [...prev, ...bulkSearch.candidates]); p.setSelectedRedactionTypes((prev) => [ ...prev, - ...Array(bulkSearch.candidates.length).fill({ - id: currentType.id, - name: currentType.name - }) + ...Array(bulkSearch.candidates.length).fill({ id: currentType.id, name: currentType.name }), ]); closePopover(); }; @@ -116,19 +107,19 @@ export const useBulkRedactionFlow = (p: { onViewPrevious: bulkSearch.goPrev, onViewNext: bulkSearch.goNext, onRedactFocused: acceptFocusedMatch, - onRedactAll: acceptAllMatches + onRedactAll: acceptAllMatches, } : undefined; const highlightLayer: THighlightLayer = { highlights: convertCandidatesToSearchHighlights(bulkSearch.candidates), - focusedId: bulkSearch.focusedCandidate?.id + focusedId: bulkSearch.focusedCandidate?.id, }; return { highlightLayer, popupProps, openPopover: (props: TRedactionPopupProps) => setPopupProps(props), - popoverProps: { onClose, onSaveSingle, bulkProps } + popoverProps: { onClose, onSaveSingle, bulkProps }, }; }; diff --git a/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/hooks/useBulkSearch.ts b/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/hooks/useBulkSearch.ts index fa8ff839..10b11bba 100644 --- a/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/hooks/useBulkSearch.ts +++ b/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/hooks/useBulkSearch.ts @@ -22,7 +22,7 @@ const wait = (ms: number, signal: AbortSignal) => clearTimeout(timeoutId); resolve(); }, - { once: true } + { once: true }, ); }); @@ -33,9 +33,7 @@ export const useBulkSearch = (p: { versionId: number; documentId: string; }) => { - const [state, setState] = useState({ - status: 'idle' - }); + const [state, setState] = useState({ status: 'idle' }); const abortRef = useRef(null); const clear = useCallback(() => { @@ -60,7 +58,7 @@ export const useBulkSearch = (p: { versionId: p.versionId, documentId: p.documentId, searchText, - signal: controller.signal + signal: controller.signal, }); if (controller.signal.aborted) return undefined; @@ -94,7 +92,7 @@ export const useBulkSearch = (p: { return undefined; } }, - [p.axiosInstance, p.urn, p.caseId, p.versionId, p.documentId] + [p.axiosInstance, p.urn, p.caseId, p.versionId, p.documentId], ); const nudge = (delta: number) => @@ -110,14 +108,12 @@ export const useBulkSearch = (p: { const removeFocused = useCallback(() => { setState((prev) => { if (prev.status !== 'done' || prev.candidates.length === 0) return prev; - const remaining = prev.candidates.filter( - (_, i) => i !== prev.focusedIndex - ); + const remaining = prev.candidates.filter((_, i) => i !== prev.focusedIndex); if (remaining.length === 0) return { status: 'idle' }; return { status: 'done', candidates: remaining, - focusedIndex: Math.min(prev.focusedIndex, remaining.length - 1) + focusedIndex: Math.min(prev.focusedIndex, remaining.length - 1), }; }); }, []); @@ -136,6 +132,6 @@ export const useBulkSearch = (p: { clear, goNext, goPrev, - removeFocused + removeFocused, }; }; diff --git a/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/hooks/useDocumentCheckOutRequest.ts b/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/hooks/useDocumentCheckOutRequest.ts index e12d09a6..131043fe 100644 --- a/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/hooks/useDocumentCheckOutRequest.ts +++ b/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/hooks/useDocumentCheckOutRequest.ts @@ -7,9 +7,7 @@ import { useAxiosInstance } from '../../../caseWorkApp/components/utils/getData' type UseDocumentCheckoutOptions = { caseId?: number; urn?: string }; const extractReadableMessageFromError = (error: unknown) => { - const errorSchema = z.object({ - response: z.object({ data: z.object({ Error: z.string() }) }) - }); + const errorSchema = z.object({ response: z.object({ data: z.object({ Error: z.string() }) }) }); const parsed = errorSchema.safeParse(error); if (!parsed.success) return null; @@ -27,7 +25,7 @@ const checkOutDocumentFromAxiosInstance = async (p: { }) => { try { const response = await p.axiosInstance.post( - `/api/urns/${p.urn}/cases/${p.caseId}/documents/${p.parentId}/versions/${p.childId}/checkout` + `/api/urns/${p.urn}/cases/${p.caseId}/documents/${p.parentId}/versions/${p.childId}/checkout`, ); return { success: true, data: { response } } as const; @@ -41,11 +39,7 @@ const checkOutDocumentFromAxiosInstance = async (p: { return { success: false, status: 'locked', message } as const; } - return { - success: false, - status: 'generic error', - message: defaultMessage - } as const; + return { success: false, status: 'generic error', message: defaultMessage } as const; } }; @@ -59,32 +53,24 @@ export const checkInDocumentFromAxiosInstance = async (p: { try { await p.axiosInstance.delete( `/api/urns/${p.urn}/cases/${p.caseId}/documents/${p.parentId}/versions/${p.childId}/checkout`, - { fetchOptions: { keepalive: true } } + { fetchOptions: { keepalive: true } }, ); return { success: true } as const; } catch (error: unknown) { - if (!!error && typeof error === 'object' && 'message' in error) - console.error(error?.message); + if (!!error && typeof error === 'object' && 'message' in error) console.error(error?.message); return { success: false } as const; } }; -export const useDocumentCheckOutRequest = ({ - caseId, - urn -}: UseDocumentCheckoutOptions) => { +export const useDocumentCheckOutRequest = ({ caseId, urn }: UseDocumentCheckoutOptions) => { const [isLoading, setIsLoading] = useState(false); const axiosInstance = useAxiosInstance(); - const checkOut = async (fnProps: { - parentId: string; - childId: number | string; - }) => { + const checkOut = async (fnProps: { parentId: string; childId: number | string }) => { if (!urn) return { success: false, message: 'no urn provided' } as const; - if (!caseId) - return { success: false, message: 'no caseId provided' } as const; + if (!caseId) return { success: false, message: 'no caseId provided' } as const; setIsLoading(true); const resp = await checkOutDocumentFromAxiosInstance({ @@ -92,7 +78,7 @@ export const useDocumentCheckOutRequest = ({ urn, caseId, parentId: fnProps.parentId, - childId: fnProps.childId + childId: fnProps.childId, }); setIsLoading(false); @@ -101,8 +87,7 @@ export const useDocumentCheckOutRequest = ({ const checkIn = async (fnProps: { parentId: string; childId: number }) => { if (!urn) return { success: false, message: 'no urn provided' } as const; - if (!caseId) - return { success: false, message: 'no caseId provided' } as const; + if (!caseId) return { success: false, message: 'no caseId provided' } as const; setIsLoading(true); const resp = await checkInDocumentFromAxiosInstance({ @@ -110,7 +95,7 @@ export const useDocumentCheckOutRequest = ({ urn, caseId, parentId: fnProps.parentId, - childId: fnProps.childId + childId: fnProps.childId, }); setIsLoading(false); diff --git a/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/utils/bulkSearchDocumentUtils.ts b/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/utils/bulkSearchDocumentUtils.ts index 94d1faf3..f82787bd 100644 --- a/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/utils/bulkSearchDocumentUtils.ts +++ b/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/utils/bulkSearchDocumentUtils.ts @@ -19,10 +19,7 @@ export type TBulkSearchResponse = { isNotFound: boolean; }; -export type TBulkSearchResult = { - status: number; - data: TBulkSearchResponse | null; -}; +export type TBulkSearchResult = { status: number; data: TBulkSearchResponse | null }; export const bulkSearchDocument = async (p: { axiosInstance: AxiosInstance; @@ -35,11 +32,7 @@ export const bulkSearchDocument = async (p: { }): Promise => { const response = await p.axiosInstance.get( `/api/urns/${p.urn}/cases/${p.caseId}/documents/${p.documentId}/versions/${p.versionId}/search`, - { - params: { SearchText: p.searchText }, - signal: p.signal, - validateStatus: () => true - } + { params: { SearchText: p.searchText }, signal: p.signal, validateStatus: () => true }, ); return { status: response.status, data: response.data ?? null }; }; diff --git a/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/utils/combineRedactionsDeletions.ts b/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/utils/combineRedactionsDeletions.ts index 02ca2351..e8608200 100644 --- a/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/utils/combineRedactionsDeletions.ts +++ b/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/utils/combineRedactionsDeletions.ts @@ -10,9 +10,7 @@ export const combineRedactionsWithRedactionDetails = (p: { }) => { const redactionsWithDetails = p.redactions .map((x) => { - const thisDetails = p.redactionDetails.find( - (y) => y.redactionId === x.id - ); + const thisDetails = p.redactionDetails.find((y) => y.redactionId === x.id); if (!thisDetails) return undefined; return { ...x, ...thisDetails }; }) diff --git a/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/utils/saveDeletionsUtils.ts b/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/utils/saveDeletionsUtils.ts index f9dcef29..f1f54746 100644 --- a/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/utils/saveDeletionsUtils.ts +++ b/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/utils/saveDeletionsUtils.ts @@ -13,13 +13,13 @@ export const saveDeletions = async (p: { redactions: [], documentModifications: p.deletions.map((red) => ({ pageIndex: red.pageNumber, - operation: 'delete' - })) + operation: 'delete', + })), }; const response = await p.axiosInstance.put( `/api/urns/${p.urn}/cases/${p.caseId}/documents/${p.parentId}/versions/${p.childId}/redact`, - payload + payload, ); return response.data; diff --git a/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/utils/saveRedactionsUtils.ts b/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/utils/saveRedactionsUtils.ts index 8d4e5e0f..f88daf7a 100644 --- a/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/utils/saveRedactionsUtils.ts +++ b/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/utils/saveRedactionsUtils.ts @@ -10,12 +10,8 @@ export const saveRedactions = async (p: { redactions: TRedaction[]; }) => { const redactionsIndexedOnPageNumber: { [k: number]: TRedaction[] } = {}; - p.redactions.forEach( - (red) => (redactionsIndexedOnPageNumber[red.pageNumber] = []) - ); - p.redactions.forEach((red) => - redactionsIndexedOnPageNumber[red.pageNumber]!.push(red) - ); + p.redactions.forEach((red) => (redactionsIndexedOnPageNumber[red.pageNumber] = [])); + p.redactions.forEach((red) => redactionsIndexedOnPageNumber[red.pageNumber]!.push(red)); const payload = { redactions: Object.values(redactionsIndexedOnPageNumber).map((reds) => { const first = reds[0]!; @@ -26,13 +22,13 @@ export const saveRedactions = async (p: { width: first.pageWidth, redactionCoordinates: reds.map((red) => { return { x1: red.x1, y1: red.y1, x2: red.x2, y2: red.y2 }; - }) + }), }; - }) + }), }; const response = await p.axiosInstance.put( `/api/urns/${p.urn}/cases/${p.caseId}/documents/${p.parentId}/versions/${p.childId}/redact`, - payload + payload, ); return response.data; diff --git a/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/utils/saveRotationsUtils.ts b/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/utils/saveRotationsUtils.ts index 9c9be17b..c46de2e8 100644 --- a/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/utils/saveRotationsUtils.ts +++ b/materials_ui/src/materials_components/CaseworkPdfRedactorWrapper/utils/saveRotationsUtils.ts @@ -13,13 +13,13 @@ export const saveRotations = async (p: { documentModifications: p.rotations.map((rotation) => ({ pageIndex: rotation.pageNumber, operation: 'rotate', - arg: rotation.rotationDegrees - })) + arg: rotation.rotationDegrees, + })), }; const response = await p.axiosInstance.post( `/api/urns/${p.urn}/cases/${p.caseId}/documents/${p.parentId}/versions/${p.childId}/modify`, - payload + payload, ); return response.data; diff --git a/materials_ui/src/materials_components/DocumentSelectAccordion/DocumentSidebar.tsx b/materials_ui/src/materials_components/DocumentSelectAccordion/DocumentSidebar.tsx index 0235ad08..26d29972 100644 --- a/materials_ui/src/materials_components/DocumentSelectAccordion/DocumentSidebar.tsx +++ b/materials_ui/src/materials_components/DocumentSelectAccordion/DocumentSidebar.tsx @@ -21,10 +21,7 @@ export const DocumentSidebar = (p: { { mode: 'accordion' } | { mode: 'notes'; documentId: string } >({ mode: 'accordion' }); - useTriggerListener({ - triggerData: p.reloadTriggerData, - fn: () => documentList.load() - }); + useTriggerListener({ triggerData: p.reloadTriggerData, fn: () => documentList.load() }); const documentList = useGetDocumentList({ urn, caseId }); useEffect(() => { @@ -49,9 +46,7 @@ export const DocumentSidebar = (p: { openDocumentIds={p.openDocumentIds} onSetActiveDocumentIds={(docIds) => p.onSetDocumentOpenIds(docIds)} onDocumentClick={p.onDocumentClick} - onNotesClick={(docId: string) => - setStatus({ mode: 'notes', documentId: docId }) - } + onNotesClick={(docId: string) => setStatus({ mode: 'notes', documentId: docId })} ActionComponent={p.ActionComponent} />
diff --git a/materials_ui/src/materials_components/DocumentSelectAccordion/DocumentSidebarAccordion.tsx b/materials_ui/src/materials_components/DocumentSelectAccordion/DocumentSidebarAccordion.tsx index 88367763..f0690620 100644 --- a/materials_ui/src/materials_components/DocumentSelectAccordion/DocumentSidebarAccordion.tsx +++ b/materials_ui/src/materials_components/DocumentSelectAccordion/DocumentSidebarAccordion.tsx @@ -1,30 +1,28 @@ import React, { useEffect, useState } from 'react'; import { DocumentSidebarAccordionDocument, - DocumentSidebarAccordionNoDocumentsAvailable + DocumentSidebarAccordionNoDocumentsAvailable, } from './DocumentSidebarAccordionDocument'; import { DocumentSidebarWrapper } from './DocumentSidebarWrapper'; import { TDocument, TDocumentList } from './getters/getDocumentList'; import { GovUkAccordionOpenCloseLinkTemplate, GovUkAccordionSectionTemplate, - GovUkAccordionTemplate + GovUkAccordionTemplate, } from './templates/GovUkAccordion'; import { categoriseDocument } from './utils/categoriseDocument'; import { categoryDetails, - initDocsOnDocCategoryNamesMap + initDocsOnDocCategoryNamesMap, } from './utils/categoriseDocumentHelperUtils'; import { safeGetDocumentSidebarReadDocIdsFromLocalStorage, - safeSetDocumentSidebarReadDocIdsFromLocalStorage + safeSetDocumentSidebarReadDocIdsFromLocalStorage, } from './utils/DocumentSidebarLocalStorageUtils'; import { areSetsEqual } from './utils/generalUtils'; -const createOpenDocumentAccordionSectionKey = (p: { - caseId: number; - sectionTitle: string; -}) => `openDocumentAccordionSection-${p.caseId}-${p.sectionTitle}`; +const createOpenDocumentAccordionSectionKey = (p: { caseId: number; sectionTitle: string }) => + `openDocumentAccordionSection-${p.caseId}-${p.sectionTitle}`; export const DocumentSidebarAccordion = (p: { caseId: number; @@ -40,9 +38,7 @@ export const DocumentSidebarAccordion = (p: { }) => { const { caseId } = p; - const [openDocumentIds, setOpenDocumentIds] = useState( - p.openDocumentIds - ); + const [openDocumentIds, setOpenDocumentIds] = useState(p.openDocumentIds); const isNoChangeInActiveDocIds = () => areSetsEqual(new Set(openDocumentIds), new Set(p.openDocumentIds)); @@ -57,13 +53,11 @@ export const DocumentSidebarAccordion = (p: { }, [openDocumentIds]); const [readDocumentIds, setReadDocumentIds] = useState( - safeGetDocumentSidebarReadDocIdsFromLocalStorage(p.caseId) + safeGetDocumentSidebarReadDocIdsFromLocalStorage(p.caseId), ); useEffect(() => { - const newReadDocIds = [ - ...new Set([...readDocumentIds, ...p.openDocumentIds]) - ]; + const newReadDocIds = [...new Set([...readDocumentIds, ...p.openDocumentIds])]; safeSetDocumentSidebarReadDocIdsFromLocalStorage({ caseId, newReadDocIds }); }, [readDocumentIds]); @@ -77,13 +71,11 @@ export const DocumentSidebarAccordion = (p: { key: x.label, label: x.label, categoryName: x.categoryName, - documents: docsOnDocCategoryNames[x.categoryName] + documents: docsOnDocCategoryNames[x.categoryName], })); const [isExpandedController, setIsExpandedController] = useState(false); - const [isExpandedSectionsTracker, setIsExpandedSectionsTracker] = useState< - boolean[] - >([]); + const [isExpandedSectionsTracker, setIsExpandedSectionsTracker] = useState([]); useEffect(() => { if (isExpandedSectionsTracker.length === 0) return; const allSectionsExpanded = isExpandedSectionsTracker.every((x) => x); @@ -115,7 +107,7 @@ export const DocumentSidebarAccordion = (p: { }} localStorageKey={createOpenDocumentAccordionSectionKey({ caseId: p.caseId, - sectionTitle: item.label + sectionTitle: item.label, })} > {item.documents.length === 0 ? ( @@ -131,20 +123,13 @@ export const DocumentSidebarAccordion = (p: { readDocumentIds={readDocumentIds} onDocumentClick={() => { p.onDocumentClick?.(document.parentId); - setReadDocumentIds((docIds) => [ - ...new Set([...docIds, document.parentId]) - ]); - const docSet = new Set([ - ...openDocumentIds, - document.parentId - ]); + setReadDocumentIds((docIds) => [...new Set([...docIds, document.parentId])]); + const docSet = new Set([...openDocumentIds, document.parentId]); setOpenDocumentIds([...docSet]); }} onNotesClick={() => p.onNotesClick(document.parentId)} ActionComponent={ - p.ActionComponent ? ( - - ) : null + p.ActionComponent ? : null } urn={p.urn} caseId={p.caseId} diff --git a/materials_ui/src/materials_components/DocumentSelectAccordion/DocumentSidebarAccordionDocument.tsx b/materials_ui/src/materials_components/DocumentSelectAccordion/DocumentSidebarAccordionDocument.tsx index 42ee9d8c..e4d41f3d 100644 --- a/materials_ui/src/materials_components/DocumentSelectAccordion/DocumentSidebarAccordionDocument.tsx +++ b/materials_ui/src/materials_components/DocumentSelectAccordion/DocumentSidebarAccordionDocument.tsx @@ -3,12 +3,7 @@ import { DocumentSidebarTag } from './DocumentSidebarTag'; import { TDocument } from './getters/getDocumentList'; import { useGetDocumentNotes } from './getters/getDocumentNotes'; import './templates/GovUkAccordion.scss'; -import { - NOTES_ARIA_LABELS, - NOTES_STATUS, - NotesIcon, - NotesStatus -} from './templates/NotesIcon'; +import { NOTES_ARIA_LABELS, NOTES_STATUS, NotesIcon, NotesStatus } from './templates/NotesIcon'; import { formatShortDate } from './utils/dateUtils'; export const DocumentSidebarAccordionNoDocumentsAvailable = () => { @@ -18,7 +13,7 @@ export const DocumentSidebarAccordionNoDocumentsAvailable = () => { borderTop: 'solid 1px #b1b4b6', background: '#ffffff', height: '60px', - padding: '12px' + padding: '12px', }} > There are no documents available. @@ -36,7 +31,7 @@ const Tooltip = (p: { text: string }) => { padding: '8px 14px', borderRadius: '6px', whiteSpace: 'nowrap', - boxShadow: '0 2px 8px rgba(0, 0, 0, 0.2)' + boxShadow: '0 2px 8px rgba(0, 0, 0, 0.2)', }} > {p.text} @@ -48,7 +43,7 @@ const Tooltip = (p: { text: string }) => { height: '0', borderLeft: '6px solid transparent', borderRight: '6px solid transparent', - borderTop: '6px solid #3d3d3d' + borderTop: '6px solid #3d3d3d', }} />
@@ -72,7 +67,7 @@ export const DocumentSidebarAccordionDocument = (p: { urn: p.urn, caseId: p.caseId, documentId: p.document.parentId, - revalidateOnMount: false + revalidateOnMount: false, }); const tooltipText = (() => { @@ -82,9 +77,7 @@ export const DocumentSidebarAccordionDocument = (p: { const firstNote = documentNotes.data[0]; - return firstNote - ? `${firstNote.text} (+${documentNotes.data.length - 1} more)` - : ''; + return firstNote ? `${firstNote.text} (+${documentNotes.data.length - 1} more)` : ''; })(); return ( @@ -96,9 +89,7 @@ export const DocumentSidebarAccordionDocument = (p: { NewVersionTag={p.newVersionDocumentId === p.document.parentId} showLeftBorder={p.activeDocumentId === p.document.parentId} showRightBorder={p.openDocumentIds.includes(p.document.parentId)} - backgroundColor={ - p.readDocumentIds.includes(p.document.parentId) ? 'white' : 'blue' - } + backgroundColor={p.readDocumentIds.includes(p.document.parentId) ? 'white' : 'blue'} // backgroundColor="blue" notesStatus={(() => { if ( @@ -143,24 +134,11 @@ export const DocumentSidebarAccordionDocumentTemplate = (p: { className={`document-select-accordion-document ${p.backgroundColor === 'blue' ? 'bg-blue' : 'bg-white'} ${p.showLeftBorder ? 'show-left-border' : ''} ${p.showRightBorder ? 'show-right-border' : ''}`} >
-
+
- {p.ActiveDocumentTag && ( - - )} + {p.ActiveDocumentTag && } {p.NewTag && } {p.NewVersionTag && } {p.ReclassifiedTag && } @@ -195,7 +173,7 @@ export const DocumentSidebarAccordionDocumentTemplate = (p: { top: '0', left: '50%', transform: 'translate(-50%, -100%)', - zIndex: 850 + zIndex: 850, }} > diff --git a/materials_ui/src/materials_components/DocumentSelectAccordion/DocumentSidebarNotes.tsx b/materials_ui/src/materials_components/DocumentSelectAccordion/DocumentSidebarNotes.tsx index cdcb7278..a12be96c 100644 --- a/materials_ui/src/materials_components/DocumentSelectAccordion/DocumentSidebarNotes.tsx +++ b/materials_ui/src/materials_components/DocumentSelectAccordion/DocumentSidebarNotes.tsx @@ -4,7 +4,7 @@ import { DocumentSidebarWrapper } from './DocumentSidebarWrapper'; import { useAxiosInstance } from './getters/getAxiosInstance'; import { postDocumentNotesFromAxiosInstance, - useGetDocumentNotes + useGetDocumentNotes, } from './getters/getDocumentNotes'; import { CloseIconButton } from './templates/CloseIconButton'; import { GovUkBanner } from './templates/GovUkBanner'; @@ -30,7 +30,7 @@ export const DocumentSidebarNotes = (p: { const documentNotes = useGetDocumentNotes({ urn: p.urn, caseId: p.caseId, - documentId: p.documentId + documentId: p.documentId, }); return ( @@ -39,12 +39,10 @@ export const DocumentSidebarNotes = (p: { style={{ borderBottom: 'solid 1px #b1b4b6', display: 'flex', - justifyContent: 'space-between' + justifyContent: 'space-between', }} > -
- Notes -
+
Notes
p.onBackButtonClick()} />
{savedSuccessfully && ( @@ -56,11 +54,7 @@ export const DocumentSidebarNotes = (p: { )}
-