From 1383e1896353791aab4bc4fad2bd50070e5f9e7e Mon Sep 17 00:00:00 2001 From: Josh-HMCTS <128602796+Josh-HMCTS@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:02:55 +0000 Subject: [PATCH 1/3] Branch frm d80548e before qm (#1811) * Add the fix and tests (#1807) * Add the fix and tests * version updated * update yarn audit * change ver num * exui-2515-case-link-issue (#1805) * checking for the availability of Other type reason * version updated * cve * version updated * version updated * Add readCookieService and language information to client context (#1780) * Add readCookieService and language information to client context * Add version * Add additional language setting on clientcontext Event guard also sets it * Make sonar advised change * Add sonar suggested change * Update npmpublish.yml * Add change to make client context always appear with language * version updated * release notes updated * version updated * version updated --------- Co-authored-by: Ritesh Dsouza * Bug/ex UI 2624 user task missing (#1822) * Update event-start.guard.ts Fix regression issue that overwrites user_task * Change version * Update event-start.guard.ts Add 'en' even if no cookie Add language to existing client_context object, not just object with user_task * Add version * Update event-start.guard.spec.ts Add helpful test * update to rel name --------- Co-authored-by: Josh * yarn audit * version name * exui-2575-MC-SRT-PR (#1798) (#1819) * Bug/ex UI 2431 special characters crashing (#1798) * Character edition before atob change * Add version * Remove control characters * Update case-edit.utils.ts * Update cases.service.ts Remove unneccessary log * Change version for tagging * Make editions to convert to HTML Entities * Change version and make slight editions after PR comments * Change version to tag * Update cases.service.ts Remove dead debug code --------- Co-authored-by: RiteshHMCTS <74713687+RiteshHMCTS@users.noreply.github.com> * exui-2469-hidden field should be removed from event submission (#1802) * hidden field should be removed from event submittion * sonar issue fix * refactor code * refactor code * Refactored to reduce chance of exception bailing out of loop --------- Co-authored-by: Andy Wilkins * exui-2536-issue-with-dynamic-multi-select-list (#1813) * exui-2536-to update-nested-dynamice-list * cve * version updated * increased scope to the dynamicList and dynamicRadioList * release notes updated * notes updated * moved repeated code to function * media viewer version update (#1812) Co-authored-by: RiteshHMCTS <74713687+RiteshHMCTS@users.noreply.github.com> * release notes updated * version updated * revert exui-2469 * updated release notes * cve * version updated * media viewer version updated for release * release notes updated --------- Co-authored-by: connorpgpmcelroy <74015088+connorpgpmcelroy@users.noreply.github.com> Co-authored-by: Andy Wilkins Co-authored-by: MunishSharmaHMCTS <112546702+MunishSharmaHMCTS@users.noreply.github.com> * cve * cve update --------- Co-authored-by: RiteshHMCTS <74713687+RiteshHMCTS@users.noreply.github.com> Co-authored-by: connorpgpmcelroy <74015088+connorpgpmcelroy@users.noreply.github.com> Co-authored-by: Ritesh Dsouza Co-authored-by: Andy Wilkins Co-authored-by: MunishSharmaHMCTS <112546702+MunishSharmaHMCTS@users.noreply.github.com> Co-authored-by: olusegun odunukan --- .github/workflows/npmpublish.yml | 2 +- RELEASE-NOTES.md | 18 +- package.json | 2 +- .../case-edit-utils/case-edit.utils.spec.ts | 64 +++ .../case-edit-utils/case-edit.utils.ts | 38 ++ .../case-editor/services/cases.service.ts | 61 +-- .../services/page-validation.service.ts | 2 +- .../event-guard/event-start.guard.spec.ts | 44 +- .../event-guard/event-start.guard.ts | 41 +- .../event-start/event-start.component.ts | 7 +- ...event-start-state-machine-context.model.ts | 3 +- .../event-start-state-machine.service.spec.ts | 7 +- .../event-start-state-machine.service.ts | 6 +- ...dynamic-radio-list-field.component.spec.ts | 25 ++ ...rite-dynamic-radio-list-field.component.ts | 4 +- .../write-linked-cases-field.component.ts | 9 +- .../src/lib/shared/services/cookie/index.ts | 1 + .../cookie/read-cookie-service.spec.ts | 21 + .../services/cookie/read-cookie-service.ts | 21 + .../form/field-type-sanitiser.spec.ts | 53 +++ .../services/form/field-type-sanitiser.ts | 37 +- .../src/lib/shared/services/index.ts | 1 + sonar-project.properties | 1 - yarn-audit-known-issues | 18 +- yarn.lock | 385 +++++++----------- 25 files changed, 559 insertions(+), 312 deletions(-) create mode 100644 projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-edit-utils/case-edit.utils.spec.ts create mode 100644 projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-edit-utils/case-edit.utils.ts create mode 100644 projects/ccd-case-ui-toolkit/src/lib/shared/services/cookie/index.ts create mode 100644 projects/ccd-case-ui-toolkit/src/lib/shared/services/cookie/read-cookie-service.spec.ts create mode 100644 projects/ccd-case-ui-toolkit/src/lib/shared/services/cookie/read-cookie-service.ts diff --git a/.github/workflows/npmpublish.yml b/.github/workflows/npmpublish.yml index 65057afdc4..bd0ecda177 100644 --- a/.github/workflows/npmpublish.yml +++ b/.github/workflows/npmpublish.yml @@ -53,7 +53,7 @@ jobs: name: code-coverage-report path: coverage - name: Analyze with SonarCloud - uses: sonarsource/sonarcloud-github-action@v3.0.0 + uses: sonarsource/sonarcloud-github-action@master env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 5c10db0ed9..f63d9717f9 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,4 +1,20 @@ -## RELEASE NOTES +## RELEASE NOTES + +### Version 7.0.75-exui-2575 +**EXUI-2536** issue with DynamicMultiSelectList +**EXUI-2431** Special characters stopping events +**EXUI-2537** upgrade pdf-dist in media viewer +**EXUI-2389** PED and Media Viewer + +### Version 7.0.75-exui-2315 +**EXUI-2315** etrieve current user language selection + + +### Version 7.0.75-exui-2515 +**EXUI-2515** case-link-issue + +### Version 7.0.75-exui-2462-rc1 +**EXUI-2462** DynamicRadioList incorrectly selects the wrong radio button ### Version 7.0.75 **EXUI-2415** Redirect to an new event by a hyperlink diff --git a/package.json b/package.json index 2a3aabeef2..c4bcc203d2 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "@edium/fsm": "^2.1.2", "@hmcts/ccpay-web-component": "6.2.1", "@hmcts/frontend": "0.0.50-alpha", - "@hmcts/media-viewer": "4.0.8", + "@hmcts/media-viewer": "4.0.9", "@ngrx/effects": "17.2.0", "@ngrx/store": "^17.2.0", "@nicky-lenaers/ngx-scroll-to": "^14.0.0", diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-edit-utils/case-edit.utils.spec.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-edit-utils/case-edit.utils.spec.ts new file mode 100644 index 0000000000..32525da16f --- /dev/null +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-edit-utils/case-edit.utils.spec.ts @@ -0,0 +1,64 @@ +import { CaseEditUtils, convertNonASCIICharacter } from "./case-edit.utils"; + +describe('CaseEditUtils', () => { + const caseUtils: CaseEditUtils = new CaseEditUtils(); + const LONG_ASCII_STRING = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890!@£$%^&*()-=[];\,./`<>?:"|{}_+'; + const LONG_PRE_STRING: string = 'Examples of non-ASCII characters: éこ¥🌍'; + const LONG_POST_STRING: string = 'Examples of non-ASCII characters: éこ¥��'; + + describe('editNonASCIICharacters', () => { + + it('should not edit undefined', () => { + // Note: Should never happen + const response = caseUtils.convertNonASCIICharacters(undefined); + expect(response).toEqual(''); + }); + + it('should not edit an empty string', () => { + const mockString = ''; + const response = caseUtils.convertNonASCIICharacters(mockString); + expect(response).toEqual(mockString); + }); + + it('should note edit ASCII characters', () => { + // note: string includes £ (non-ASCII) which should not be edited + const response = caseUtils.convertNonASCIICharacters(LONG_ASCII_STRING); + expect(response).toEqual(LONG_ASCII_STRING); + }); + + it('should not edit £ (non ASCII)', () => { + const mockString = 'Cost: £2.50'; + const response = caseUtils.convertNonASCIICharacters(mockString); + expect(response).toEqual(mockString); + }); + + it('should edit ASCII characters', () => { + // Summarises with copied mock string + const response = caseUtils.convertNonASCIICharacters(LONG_PRE_STRING); + expect(response).toEqual(LONG_POST_STRING); + + // Goes deeper into what should be happening just in case + const chineseCharacter = '漢'; + const secondMockString = 'Examples of non-ASCII characters: ' + chineseCharacter; + const editedSecondMockString = + `Examples of non-ASCII characters: ${CaseEditUtils.PREFIX + chineseCharacter.charCodeAt(0) + CaseEditUtils.SUFFIX}`; + const secondResponse = caseUtils.convertNonASCIICharacters(secondMockString); + expect(secondResponse).toEqual(editedSecondMockString); + }); + }); + + describe('revertEditNonASCIICharacters', () => { + + it('should not revert strings without the prefix and/or suffix', () => { + const mockString = 'Hello World!'; + const response = caseUtils.convertHTMLEntities(mockString); + expect(response).toEqual(mockString); + }); + + it('should revert relevant strings', () => { + const response = caseUtils.convertHTMLEntities(LONG_POST_STRING); + expect(response).toEqual(LONG_PRE_STRING); + }); + }); + +}); diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-edit-utils/case-edit.utils.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-edit-utils/case-edit.utils.ts new file mode 100644 index 0000000000..c5c42061d6 --- /dev/null +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-edit-utils/case-edit.utils.ts @@ -0,0 +1,38 @@ + +export function convertNonASCIICharacter(character: string): string { + if (character === '£') { + // pound sign will be frequently used and works for btoa despite being non-ASCII + // note: this could be done for other characters provided they work for btoa() + return character; + } + // Note: Will convert to HTML entity + return CaseEditUtils.PREFIX + character.charCodeAt(0) + CaseEditUtils.SUFFIX; +} + +export class CaseEditUtils { + + public static readonly PREFIX = '&#'; + public static readonly SUFFIX = ';'; + + public convertNonASCIICharacters(rawString: string): string { + return rawString ? rawString.replace(/[^\x20-\x7E]/g, function (c) { + return convertNonASCIICharacter(c); + }) : ''; + } + + public convertHTMLEntities(editedString: string): string { + const revertedCharacterList = editedString.split(CaseEditUtils.PREFIX); + let rawString = revertedCharacterList[0]; + for (let index = 1; index < revertedCharacterList.length; index++) { + const currentSection = revertedCharacterList[index]; + if (!currentSection.includes(CaseEditUtils.SUFFIX)) { + return rawString.concat(currentSection); + } else { + const suffixSplitList = currentSection.split(CaseEditUtils.SUFFIX); + const characterCode = Number(suffixSplitList[0]); + rawString = rawString.concat(String.fromCharCode(characterCode), suffixSplitList[1]); + } + } + return rawString; + } +} diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/services/cases.service.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/services/cases.service.ts index 87edcd258d..299d0d8743 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/services/cases.service.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/services/cases.service.ts @@ -21,6 +21,7 @@ import { LinkedCasesResponse } from '../../palette/linked-cases/domain/linked-ca import { CaseAccessUtils } from '../case-access-utils'; import { WizardPage } from '../domain'; import { WizardPageFieldToCaseFieldMapper } from './wizard-page-field-to-case-field.mapper'; +import { CaseEditUtils } from '../case-edit-utils/case-edit.utils'; @Injectable() export class CasesService { @@ -396,8 +397,10 @@ export class CasesService { private addClientContextHeader(headers: HttpHeaders): HttpHeaders { const clientContextDetails = this.sessionStorageService.getItem('clientContext'); if (clientContextDetails) { - // may require URI encoding in certain circumstances - const clientContext = window.btoa(clientContextDetails); + const caseEditUtils = new CaseEditUtils(); + // below changes non-ASCII characters + const editedClientContext = caseEditUtils.convertNonASCIICharacters(clientContextDetails); + const clientContext = window.btoa(editedClientContext); if (clientContext) { headers = headers.set('Client-Context', clientContext); } @@ -406,59 +409,13 @@ export class CasesService { } private updateClientContextStorage(headers: HttpHeaders): void { - // for mocking - TODO: Kasi Remove/Uncomment for testing - // headers = this.setMockClientContextHeader(headers); if (headers && headers.get('Client-Context')) { + const caseEditUtils = new CaseEditUtils(); const clientContextString = window.atob(headers.get('Client-Context')); - this.sessionStorageService.setItem('clientContext', clientContextString); + // below reverts non-ASCII characters + const editedClientContextString = caseEditUtils.convertHTMLEntities(clientContextString); + this.sessionStorageService.setItem('clientContext', editedClientContextString); } } - // for mocking - TODO: Kasi Remove/Uncomment for testing - /* private setMockClientContextHeader(headers: HttpHeaders): HttpHeaders { - const mockClientContext = { client_context: { - user_task: { - task_data: { - // Replace with relevant task id to complete other/current task - id: "2c7e03cc-18e8-11ef-bfd0-763319b21cea", - // Can edit other details to check they update - name: "Review the appeal - Test mocked", - assignee: "dfd4c2d1-67b1-40f9-8680-c9551632f5d9", - type: "reviewTheAppeal", - task_state: "assigned", - task_system: "SELF", - security_classification: "PUBLIC", - task_title: "Review the appeal", - created_date: "2024-05-23T09:38:12+0000", - due_date: "2024-05-28T09:39:00+0000", - location_name: "Taylor House", - location: "765324", - execution_type: "Case Management Task", - jurisdiction: "IA", - region: "1", - case_type_id: "Asylum", - case_id: "1716456926502698", - case_category: "Protection", - case_name: "Aipp Check", - auto_assigned: false, - warnings: false, - warning_list: { values: [] }, - case_management_category: "Protection", - work_type_id: "decision_making_work", - work_type_label: "Decision-making work", - permissions: { values : ["Read","Own","Manage","Execute","Cancel","Complete","Claim","Assign","Unassign"] }, - description: "[Request respondent evidence](/case/IA/Asylum/${[CASE_REFERENCE]}/trigger/requestRespondentEvidence)", - role_category: "LEGAL_OPERATIONS", - minor_priority: 500, - major_priority: 5000, - priority_date: "2024-05-28T09:39:00+0000" - }, - // determines whether task will be completed - sets default to true via EXUI - complete_task: true - } - }}; - const encodedMockedClientContext = window.btoa(JSON.stringify(mockClientContext)); - headers = headers.set('Client-Context', encodedMockedClientContext); - return headers; - } */ } diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/services/page-validation.service.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/services/page-validation.service.ts index e4d13b4780..172d7443ec 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/services/page-validation.service.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/services/page-validation.service.ts @@ -16,7 +16,7 @@ export class PageValidationService { page.case_fields .filter(caseField => !this.caseFieldService.isReadOnly(caseField)) .filter(caseField => !this.isHidden(caseField, editForm)) - .map(caseField => { + .forEach(caseField => { const theControl = FieldsUtils.isCaseFieldOfType(caseField, ['JudicialUser']) ? editForm.controls['data'].get(`${caseField.id}_judicialUserControl`) : editForm.controls['data'].get(caseField.id); diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/event-start/event-guard/event-start.guard.spec.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/event-start/event-guard/event-start.guard.spec.ts index 693e3b2642..8636750f2a 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/event-start/event-guard/event-start.guard.spec.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/event-start/event-guard/event-start.guard.spec.ts @@ -3,7 +3,7 @@ import { ActivatedRouteSnapshot, Router } from '@angular/router'; import { of } from 'rxjs'; import { TaskPayload } from '../../../domain/work-allocation/TaskPayload'; import { UserInfo } from '../../../domain/user/user-info.model'; -import { SessionStorageService } from '../../../services'; +import { ReadCookieService, SessionStorageService } from '../../../services'; import { WorkAllocationService } from '../../case-editor'; import { EventStartGuard } from './event-start.guard'; import { AbstractAppConfig } from '../../../../app.config'; @@ -30,12 +30,14 @@ describe('EventStartGuard', () => { let service: jasmine.SpyObj; let router: jasmine.SpyObj; let sessionStorageService: jasmine.SpyObj; + let mockCookieService: jasmine.SpyObj; let mockAbstractConfig: jasmine.SpyObj; beforeEach(() => { service = jasmine.createSpyObj('WorkAllocationService', ['getTasksByCaseIdAndEventId']); router = jasmine.createSpyObj('Router', ['navigate']); sessionStorageService = jasmine.createSpyObj('SessionStorageService', ['getItem', 'setItem', 'removeItem']); + mockCookieService = jasmine.createSpyObj('readCookieService', ['getCookie']); mockAbstractConfig = jasmine.createSpyObj('AbstractAppConfig', ['logMessage']); TestBed.configureTestingModule({ @@ -44,7 +46,8 @@ describe('EventStartGuard', () => { { provide: WorkAllocationService, useValue: service }, { provide: Router, useValue: router }, { provide: SessionStorageService, useValue: sessionStorageService }, - { provide: AbstractAppConfig, useValue: mockAbstractConfig } + { provide: AbstractAppConfig, useValue: mockAbstractConfig }, + { provide: ReadCookieService, useValue: mockCookieService } ] }); @@ -64,6 +67,33 @@ describe('EventStartGuard', () => { }); }); + it('client context should be set with language regardless whether task is attached to event', () => { + sessionStorageService.getItem.and.returnValue(null); + const mockClientContext = { client_context: { user_language: { language: 'cookieString' } } }; + mockCookieService.getCookie.and.returnValue('cookieString'); + const route = createActivatedRouteSnapshot('caseId', 'eventId'); + const result$ = guard.canActivate(route); + result$.subscribe(result => { + expect(result).toEqual(false); + // check client context is set correctly + expect(sessionStorageService.setItem).toHaveBeenCalledWith('clientContext', JSON.stringify(mockClientContext)); + }); + }); + + it('client context should be set with language regardless if client context already exists', () => { + const mockClientContext: any = { client_context: { user_task: {}, additional_field: 'test' } }; + sessionStorageService.getItem.and.returnValues(null, null, JSON.stringify(mockClientContext)); + mockCookieService.getCookie.and.returnValue('cookieString'); + const route = createActivatedRouteSnapshot('caseId', 'eventId'); + const result$ = guard.canActivate(route); + mockClientContext.client_context.user_language = { language: 'cookieString' }; + result$.subscribe(result => { + expect(result).toEqual(false); + // check client context is set correctly + expect(sessionStorageService.setItem).toHaveBeenCalledWith('clientContext', JSON.stringify(mockClientContext)); + }); + }); + it('should log a message and not call getTasksByCaseIdAndEventId when caseInfo is not available', () => { sessionStorageService.getItem.and.returnValue(null); const route = createActivatedRouteSnapshot('caseId', 'eventId'); @@ -136,17 +166,22 @@ describe('EventStartGuard', () => { }); it('should return true and navigate to event trigger if one task is assigned to user', () => { + const mockLanguage = 'en'; const clientContext = { client_context: { user_task: { task_data: tasks[0], complete_task: true + }, + user_language: { + language: mockLanguage } } } tasks[0].assignee = '1'; const mockPayload: TaskPayload = {task_required_for_event: false, tasks}; sessionStorageService.getItem.and.returnValue(JSON.stringify(getExampleUserInfo())); + mockCookieService.getCookie.and.returnValue(mockLanguage); expect(guard.checkTaskInEventNotRequired(mockPayload, caseId, null)).toBe(true); expect(sessionStorageService.setItem).toHaveBeenCalledWith('clientContext', JSON.stringify(clientContext)); }); @@ -161,11 +196,15 @@ describe('EventStartGuard', () => { }); it('should return true and navigate to event trigger if navigated to via task next steps', () => { + const mockLanguage = 'cy'; const clientContext = { client_context: { user_task: { task_data: tasks[0], complete_task: true + }, + user_language: { + language: mockLanguage } } } @@ -173,6 +212,7 @@ describe('EventStartGuard', () => { tasks.push(tasks[0]); const mockPayload: TaskPayload = {task_required_for_event: false, tasks}; sessionStorageService.getItem.and.returnValue(JSON.stringify(getExampleUserInfo())); + mockCookieService.getCookie.and.returnValue(mockLanguage); expect(guard.checkTaskInEventNotRequired(mockPayload, caseId, '0d22d838-b25a-11eb-a18c-f2d58a9b7bc6')).toBe(true); expect(sessionStorageService.setItem).toHaveBeenCalledWith('clientContext', JSON.stringify(clientContext)); }); diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/event-start/event-guard/event-start.guard.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/event-start/event-guard/event-start.guard.ts index 81feae8093..2472f22c03 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/event-start/event-guard/event-start.guard.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/event-start/event-guard/event-start.guard.ts @@ -6,7 +6,7 @@ import { switchMap } from 'rxjs/operators'; import { AbstractAppConfig } from '../../../../app.config'; import { TaskEventCompletionInfo } from '../../../domain/work-allocation/Task'; import { TaskPayload } from '../../../domain/work-allocation/TaskPayload'; -import { SessionStorageService } from '../../../services'; +import { ReadCookieService, SessionStorageService } from '../../../services'; import { WorkAllocationService } from '../../case-editor'; @Injectable() @@ -16,7 +16,8 @@ export class EventStartGuard implements CanActivate { constructor(private readonly workAllocationService: WorkAllocationService, private readonly router: Router, private readonly sessionStorageService: SessionStorageService, - private readonly abstractConfig: AbstractAppConfig) { + private readonly abstractConfig: AbstractAppConfig, + private readonly cookieService: ReadCookieService) { } public canActivate(route: ActivatedRouteSnapshot): Observable { @@ -30,6 +31,34 @@ export class EventStartGuard implements CanActivate { userId = userInfo.id ? userInfo.id : userInfo.uid; } const caseInfoStr = this.sessionStorageService.getItem('caseInfo'); + const languageCookie = this.cookieService.getCookie('exui-preferred-language'); + const currentLanguage = !!languageCookie && languageCookie !== '' ? languageCookie : 'en'; + const preClientContext = this.sessionStorageService.getItem(EventStartGuard.CLIENT_CONTEXT); + if (!preClientContext) { + // creates client context for language if not already existing + const storeClientContext = { + client_context: { + user_language: { + language: currentLanguage + } + } + }; + this.sessionStorageService.setItem(EventStartGuard.CLIENT_CONTEXT, JSON.stringify(storeClientContext)); + } else { + const clientContextObj = JSON.parse(preClientContext); + if (!clientContextObj?.client_context?.user_language) { + const clientContextAddLanguage = { + ...clientContextObj, + client_context: { + ...clientContextObj.client_context, + user_language: { + language: currentLanguage + } + } + } + this.sessionStorageService.setItem(EventStartGuard.CLIENT_CONTEXT, JSON.stringify(clientContextAddLanguage)); + } + } if (caseInfoStr) { const caseInfo = JSON.parse(caseInfoStr); if (caseInfo && caseInfo.cid === caseId) { @@ -75,12 +104,16 @@ export class EventStartGuard implements CanActivate { } else { task = tasksAssignedToUser[0]; } + const currentLanguage = this.cookieService.getCookie('exui-preferred-language'); // if one task assigned to user, allow user to complete event const storeClientContext = { client_context: { user_task: { task_data: task, complete_task: true + }, + user_language: { + language: currentLanguage } } }; @@ -105,11 +138,15 @@ export class EventStartGuard implements CanActivate { taskId: task.id, createdTimestamp: Date.now() }; + const currentLanguage = this.cookieService.getCookie('exui-preferred-language'); const storeClientContext = { client_context: { user_task: { task_data: task, complete_task: true + }, + user_language: { + language: currentLanguage } } }; diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/event-start/event-start.component.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/event-start/event-start.component.ts index c02d551db7..a291ca8d72 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/event-start/event-start.component.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/event-start/event-start.component.ts @@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { StateMachine } from '@edium/fsm'; import { Task } from '../../domain/work-allocation/Task'; +import { ReadCookieService } from '../../services/cookie/read-cookie-service'; import { SessionStorageService } from '../../services/session/session-storage.service'; import { EventStartStateMachineContext } from './models/event-start-state-machine-context.model'; import { EventStartStateMachineService } from './services/event-start-state-machine.service'; @@ -18,7 +19,8 @@ export class EventStartComponent implements OnInit { constructor(private service: EventStartStateMachineService, private readonly router: Router, private readonly route: ActivatedRoute, - private readonly sessionStorageService: SessionStorageService) { + private readonly sessionStorageService: SessionStorageService, + private readonly cookieService: ReadCookieService) { } public ngOnInit(): void { @@ -36,7 +38,8 @@ export class EventStartComponent implements OnInit { taskId, router: this.router, route: this.route, - sessionStorageService: this.sessionStorageService + sessionStorageService: this.sessionStorageService, + cookieService: this.cookieService }; // Initialise state machine diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/event-start/models/event-start-state-machine-context.model.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/event-start/models/event-start-state-machine-context.model.ts index 0cfd149b0b..2470a68119 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/event-start/models/event-start-state-machine-context.model.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/event-start/models/event-start-state-machine-context.model.ts @@ -1,6 +1,6 @@ import { ActivatedRoute, Router } from '@angular/router'; import { Task } from '../../../domain/work-allocation/Task'; -import { SessionStorageService } from '../../../services'; +import { ReadCookieService, SessionStorageService } from '../../../services'; export interface EventStartStateMachineContext { tasks: Task[]; @@ -10,4 +10,5 @@ export interface EventStartStateMachineContext { router: Router; route: ActivatedRoute; sessionStorageService: SessionStorageService; + cookieService: ReadCookieService; } diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/event-start/services/event-start-state-machine.service.spec.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/event-start/services/event-start-state-machine.service.spec.ts index d0a6cdb145..d2ea4c7e81 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/event-start/services/event-start-state-machine.service.spec.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/event-start/services/event-start-state-machine.service.spec.ts @@ -3,7 +3,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; import { StateMachine } from '@edium/fsm'; import { Task } from '../../../domain/work-allocation/Task'; -import { SessionStorageService } from '../../../services'; +import { ReadCookieService, SessionStorageService } from '../../../services'; import { EventStartStateMachineContext, EventStartStates } from '../models'; import { EventStartStateMachineService } from './event-start-state-machine.service'; import createSpyObj = jasmine.createSpyObj; @@ -11,6 +11,7 @@ import createSpyObj = jasmine.createSpyObj; describe('EventStartStateMachineService', () => { let service: EventStartStateMachineService; let stateMachine: StateMachine; + let mockReadCookieService: any; let mockSessionStorageService: any; // tslint:disable-next-line: prefer-const let mockRoute: ActivatedRoute; @@ -109,6 +110,7 @@ describe('EventStartStateMachineService', () => { ]; mockSessionStorageService = createSpyObj('sessionStorageService', ['getItem', 'setItem']); + mockReadCookieService = createSpyObj('readCookieService', ['getCookie']); mockSessionStorageService.getItem.and.returnValue(`{"id": "test-user-id", "forename": "Test", "surname": "User", "roles": ["caseworker-role1", "caseworker-role3"], "email": "test@mail.com", "token": null}`); @@ -119,7 +121,8 @@ describe('EventStartStateMachineService', () => { taskId: '1122-3344-5566-7788', router: mockRouter, route: mockRoute, - sessionStorageService: mockSessionStorageService + sessionStorageService: mockSessionStorageService, + cookieService: mockReadCookieService }; beforeEach(async () => { diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/event-start/services/event-start-state-machine.service.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/event-start/services/event-start-state-machine.service.ts index 8ab746f4b4..5b95b4cf08 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/event-start/services/event-start-state-machine.service.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/event-start/services/event-start-state-machine.service.ts @@ -184,14 +184,18 @@ export class EventStartStateMachineService { task = context.tasks[0]; } - const taskStr= JSON.stringify(task); + const taskStr = JSON.stringify(task); console.log('entryActionForStateOneTaskAssignedToUser: setting client context task_data to ' + taskStr); // Store task to session + const currentLanguage = context.cookieService.getCookie('exui-preferred-language'); const clientContext = { client_context: { user_task: { task_data: task, complete_task: true + }, + user_language: { + language: currentLanguage } } }; diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/dynamic-radio-list/write-dynamic-radio-list-field.component.spec.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/dynamic-radio-list/write-dynamic-radio-list-field.component.spec.ts index 95e6530a43..e19097d53f 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/dynamic-radio-list/write-dynamic-radio-list-field.component.spec.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/dynamic-radio-list/write-dynamic-radio-list-field.component.spec.ts @@ -98,4 +98,29 @@ describe('WriteDynamicRadioListFieldComponent', () => { expect(attr(options[1], 'id')).toEqual('MaritalStatus_F'); expect(attr(options[2], 'id')).toEqual('MaritalStatus_O'); }); + + // Note: Currently unknown why this is the default for a parent with relevant value + it('test parent value sets element ID', () => { + component.parent = {value: {id: '1', value: 'value'}} as any; + fixture.detectChanges(); + const options = de.queryAll($RADIO); + + expect(options.length).toEqual(3); + expect(attr(options[0], 'type')).toEqual('radio'); + expect(attr(options[0], 'id')).toEqual('1value'); + expect(attr(options[1], 'id')).toEqual('1value'); + expect(attr(options[2], 'id')).toEqual('1value'); + }); + + it('test parent value not present', () => { + component.parent = {value: null} as any; + fixture.detectChanges(); + const options = de.queryAll($RADIO); + + expect(options.length).toEqual(3); + expect(attr(options[0], 'type')).toEqual('radio'); + expect(attr(options[0], 'id')).toEqual('MaritalStatus_M'); + expect(attr(options[1], 'id')).toEqual('MaritalStatus_F'); + expect(attr(options[2], 'id')).toEqual('MaritalStatus_O'); + }); }); diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/dynamic-radio-list/write-dynamic-radio-list-field.component.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/dynamic-radio-list/write-dynamic-radio-list-field.component.ts index add27f771b..a36f9cf641 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/dynamic-radio-list/write-dynamic-radio-list-field.component.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/dynamic-radio-list/write-dynamic-radio-list-field.component.ts @@ -33,6 +33,8 @@ export class WriteDynamicRadioListFieldComponent extends AbstractFieldWriteCompo } public createElementId(name: string): string { - return this.parent && this.parent.value ? this.parent.value.id + this.parent.value.value : super.createElementId(name); + // EXUI-2462 - parent may not always have value with content + // this is independent from the caseField.list_items so is irrelevant to event journey + return this.parent?.value?.id ? this.parent.value.id + this.parent.value.value : super.createElementId(name); } } diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/linked-cases/write-linked-cases-field.component.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/linked-cases/write-linked-cases-field.component.ts index 0e16f51629..27989afaca 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/linked-cases/write-linked-cases-field.component.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/linked-cases/write-linked-cases-field.component.ts @@ -98,10 +98,13 @@ export class WriteLinkedCasesFieldComponent extends AbstractFieldWriteComponent next: (reasons) => { // Sort in ascending order const linkCaseReasons = reasons.list_of_values.sort((a, b) => (a.value_en > b.value_en) ? 1 : -1); - - this.linkedCasesService.linkCaseReasons = linkCaseReasons?.filter((reason) => reason.value_en !== 'Other'); + if (linkCaseReasons?.filter((reason) => reason.value_en !== 'Other').length > 0) { + this.linkedCasesService.linkCaseReasons = linkCaseReasons?.filter((reason) => reason.value_en !== 'Other'); + } // Move Other option to the end of the list - this.linkedCasesService.linkCaseReasons.push(linkCaseReasons?.find((reason) => reason.value_en === 'Other')); + if (linkCaseReasons?.find((reason) => reason.value_en === 'Other')) { + this.linkedCasesService.linkCaseReasons.push(linkCaseReasons?.find((reason) => reason.value_en === 'Other')); + } } }); } diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/services/cookie/index.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/services/cookie/index.ts new file mode 100644 index 0000000000..af017e92dd --- /dev/null +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/services/cookie/index.ts @@ -0,0 +1 @@ +export * from './read-cookie-service'; \ No newline at end of file diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/services/cookie/read-cookie-service.spec.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/services/cookie/read-cookie-service.spec.ts new file mode 100644 index 0000000000..d68b07870c --- /dev/null +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/services/cookie/read-cookie-service.spec.ts @@ -0,0 +1,21 @@ +import { ReadCookieService } from "./read-cookie-service"; + +describe('CookieService', () => { + const mockDocument: any = { + cookie: '' + }; + + const cookieService: ReadCookieService = new ReadCookieService(mockDocument); + + afterEach(() => { + mockDocument.cookie = ''; + }); + + it('should get a cookie', () => { + + mockDocument.cookie = 'user=dummy'; + const result = cookieService.getCookie('user'); + expect(result).toBe('dummy'); + }); + +}); diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/services/cookie/read-cookie-service.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/services/cookie/read-cookie-service.ts new file mode 100644 index 0000000000..34739d5365 --- /dev/null +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/services/cookie/read-cookie-service.ts @@ -0,0 +1,21 @@ +import { DOCUMENT } from '@angular/common'; +import { Inject, Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class ReadCookieService { + private readonly document?: Document; + + constructor(@Inject(DOCUMENT) doc?: any) { + this.document = doc as Document; + } + + public getCookie(key: string): string { + const cookieValue = this.document.cookie + .split('; ') + .find(row => row.startsWith(`${key}=`)) + .split('=')[1]; + return cookieValue; + } +} diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/services/form/field-type-sanitiser.spec.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/services/form/field-type-sanitiser.spec.ts index 34f5bafa5d..44bf4be5a6 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/services/form/field-type-sanitiser.spec.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/services/form/field-type-sanitiser.spec.ts @@ -216,6 +216,36 @@ describe('FieldTypeSanitiser', () => { display_context: 'MANDATORY' }] } + }, + { + id: 'field1', + field_type: { + type: 'Complex', + complex_fields: [ + { + id: 'subfield1', + field_type: { + type: 'Complex', + complex_fields: [ + { + id: 'subsubfield1', + field_type: { + type: 'DynamicMultiSelectList', + }, + display_context: 'MANDATORY', + }, + ], + }, + }, + ], + }, + _value: { + subfield1: { + subsubfield1: { + list_items: ['nestedItem1', 'nestedItem2'], + }, + }, + }, } ] as unknown as CaseField[]; }); @@ -274,5 +304,28 @@ describe('FieldTypeSanitiser', () => { { code: '192', label: 'Peter Pan' } ]); }); + + it('should add list_items to type DynamicMultiSelectList in deeply nested field', () => { + const result = fieldTypeSanitiser.ensureDynamicMultiSelectListPopulated(mockCaseFields); + expect(result[2].field_type.complex_fields[0].field_type.complex_fields[0].list_items).toEqual(['nestedItem1', 'nestedItem2']); + }); + + it('should add list_items to type DynamicRadioButtonList in deeply nested field', () => { + mockCaseFields[2].field_type.complex_fields[0].field_type.complex_fields[0].field_type.type = 'DynamicRadioList'; + const result = fieldTypeSanitiser.ensureDynamicMultiSelectListPopulated(mockCaseFields); + expect(result[2].field_type.complex_fields[0].field_type.complex_fields[0].list_items).toEqual(['nestedItem1', 'nestedItem2']); + }); + + it('should add list_items to type DynamicList in deeply nested field', () => { + mockCaseFields[2].field_type.complex_fields[0].field_type.complex_fields[0].field_type.type = 'DynamicList'; + const result = fieldTypeSanitiser.ensureDynamicMultiSelectListPopulated(mockCaseFields); + expect(result[2].field_type.complex_fields[0].field_type.complex_fields[0].list_items).toEqual(['nestedItem1', 'nestedItem2']); + }); + + it('should add list_items to type FixedList in deeply nested field', () => { + mockCaseFields[2].field_type.complex_fields[0].field_type.complex_fields[0].field_type.type = 'FixedList'; + const result = fieldTypeSanitiser.ensureDynamicMultiSelectListPopulated(mockCaseFields); + expect(result[2].field_type.complex_fields[0].field_type.complex_fields[0].list_items).toBeUndefined(); + }); }); }); diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/services/form/field-type-sanitiser.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/services/form/field-type-sanitiser.ts index 22efe39d13..16ba7ef5df 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/services/form/field-type-sanitiser.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/services/form/field-type-sanitiser.ts @@ -9,6 +9,7 @@ export class FieldTypeSanitiser { public static readonly FIELD_TYPE_DYNAMIC_LIST: FieldTypeEnum = 'DynamicList'; public static readonly FIELD_TYPE_DYNAMIC_RADIO_LIST: FieldTypeEnum = 'DynamicRadioList'; public static readonly FIELD_TYPE_DYNAMIC_MULTISELECT_LIST: FieldTypeEnum = 'DynamicMultiSelectList'; + public static readonly DYNAMIC_LIST_TYPE: FieldTypeEnum[] = ['DynamicList', 'DynamicRadioList', 'DynamicMultiSelectList']; /** * This method finds dynamiclists in a form and replaces their string * values, with a JSON object, as below: @@ -59,17 +60,41 @@ export class FieldTypeSanitiser { if (field.field_type.type !== 'Complex') { return field; } - const complexFieldsUpdated = field.field_type.complex_fields.map((complexField) => - complexField.field_type.type === FieldTypeSanitiser.FIELD_TYPE_DYNAMIC_MULTISELECT_LIST && complexField.display_context !== 'HIDDEN' && field._value?.[complexField.id] - ? { ...complexField, list_items: field._value[complexField.id]?.list_items } : complexField - ); - return { ...field, field_type: { ...field.field_type, complex_fields: complexFieldsUpdated } } as CaseField; + const caseFieldData = field._value; + // Process each complex field + field.field_type.complex_fields.forEach((complexField) => { + if (complexField.field_type.type === FieldTypeSanitiser.FIELD_TYPE_COMPLEX) { + this.checkNestedDynamicList(complexField, caseFieldData?.[complexField.id]); + } else if (this.isDynamicList(complexField.field_type.type) && + complexField.display_context !== 'HIDDEN' && + field._value?.[complexField.id] + ) { + complexField.list_items = field._value[complexField.id]?.list_items; + } + }); + // Final transformation: construct updated field object + return { ...field, field_type: { ...field?.field_type } } as CaseField; + }); + } + private checkNestedDynamicList(caseField: CaseField, fieldData: any = null): void { + caseField.field_type.complex_fields.forEach((complexField) => { + if (complexField.field_type.type === FieldTypeSanitiser.FIELD_TYPE_COMPLEX) { + this.checkNestedDynamicList(complexField, fieldData?.[complexField.id]); + } else if (this.isDynamicList(complexField.field_type.type) && + complexField.display_context !== 'HIDDEN' && + fieldData?.[complexField.id] + ) { + complexField.list_items = fieldData?.[complexField.id]?.list_items; + } }); } + private isDynamicList(fieldType: FieldTypeEnum): boolean { + return FieldTypeSanitiser.DYNAMIC_LIST_TYPE.indexOf(fieldType) !== -1; + } + private convertArrayToDynamicListOutput(field: CaseField, data: any): void { const values = data[field.id]; - if (Array.isArray(values)) { const listItems = this.getListItems(field); const matches = listItems.filter(item => values.map(v => v.code).indexOf(item.code) !== -1); diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/services/index.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/services/index.ts index f36a406324..060ab28f09 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/services/index.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/services/index.ts @@ -5,6 +5,7 @@ export * from './auth'; export * from './case-fields'; export * from './case-file-view'; export * from './case-flag'; +export * from './cookie'; export * from './document-management'; export * from './draft'; export * from './error'; diff --git a/sonar-project.properties b/sonar-project.properties index 9c0f09a1fd..2fd4084099 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -8,5 +8,4 @@ sonar.test.inclusions=projects/ccd-case-ui-toolkit/src/**/*.spec.ts sonar.exclusions=projects/ccd-case-ui-toolkit/src/*.spec.ts, projects/ccd-case-ui-toolkit/src/**/*.module.ts, projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/datetime-picker/datetime-picker-component.spec.ts, projects/ccd-case-ui-toolkit/src/lib/shared/test/mock-rpx-translate.pipe.ts, projects/ccd-case-ui-toolkit/src/test.ts, projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/case-flag/write-case-flag-field.component.ts, projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/datetime-picker/datetime-picker-component.ts, projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/complex/read-complex-field.component.ts, projects/ccd-case-ui-toolkit/src/lib/shared/commons/address-validation-constants.ts, projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/markdown/markdown.component.ts sonar.javascript.lcov.reportPaths=/github/workspace/coverage/ccd-case-ui-toolkit/lcov-report/lcov.info -sonar.host.url= sonar.sourceEncoding=UTF-8 diff --git a/yarn-audit-known-issues b/yarn-audit-known-issues index d19f2e9d5d..0791c5b5f0 100644 --- a/yarn-audit-known-issues +++ b/yarn-audit-known-issues @@ -5,9 +5,11 @@ {"value":"cookie","children":{"ID":1099846,"Issue":"cookie accepts cookie name, path, and domain with out of bounds characters","URL":"https://github.com/advisories/GHSA-pxg6-pf52-xh8x","Severity":"low","Vulnerable Versions":"<0.7.0","Tree Versions":["0.5.0"],"Dependents":["express@npm:4.18.2"]}} {"value":"copy-concurrently","children":{"ID":"copy-concurrently (deprecation)","Issue":"This package is no longer supported.","Severity":"moderate","Vulnerable Versions":"1.0.5","Tree Versions":["1.0.5"],"Dependents":["move-concurrently@npm:1.0.1"]}} {"value":"core-js","children":{"ID":"core-js (deprecation)","Issue":"core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.","Severity":"moderate","Vulnerable Versions":"1.2.7","Tree Versions":["1.2.7"],"Dependents":["fbjs@npm:0.8.18"]}} +{"value":"cross-spawn","children":{"ID":1100562,"Issue":"Regular Expression Denial of Service (ReDoS) in cross-spawn","URL":"https://github.com/advisories/GHSA-3xgq-45jj-v275","Severity":"high","Vulnerable Versions":"<6.0.6","Tree Versions":["5.1.0"],"Dependents":["execa@npm:0.7.0"]}} +{"value":"cross-spawn","children":{"ID":1100563,"Issue":"Regular Expression Denial of Service (ReDoS) in cross-spawn","URL":"https://github.com/advisories/GHSA-3xgq-45jj-v275","Severity":"high","Vulnerable Versions":">=7.0.0 <7.0.5","Tree Versions":["7.0.3"],"Dependents":["foreground-child@npm:3.1.1"]}} {"value":"domexception","children":{"ID":"domexception (deprecation)","Issue":"Use your platform's native DOMException instead","Severity":"moderate","Vulnerable Versions":"4.0.0","Tree Versions":["4.0.0"],"Dependents":["jsdom@virtual:ce56289c4b7a2e9003d709997e253c1c80dcaee4c6fbe440cbe9ba5de5db8af3a7b7ad41bbdec5a5e3d40dc9c3c54bef92dd6885ff84cd436d636d5a1b380a61#npm:20.0.3"]}} {"value":"express","children":{"ID":1096820,"Issue":"Express.js Open Redirect in malformed URLs","URL":"https://github.com/advisories/GHSA-rv95-896h-c2vc","Severity":"moderate","Vulnerable Versions":"<4.19.2","Tree Versions":["4.18.2"],"Dependents":["json-server@npm:0.15.1"]}} -{"value":"express","children":{"ID":1099529,"Issue":"express vulnerable to XSS via response.redirect()","URL":"https://github.com/advisories/GHSA-qw6h-vgh9-j6wx","Severity":"moderate","Vulnerable Versions":"<4.20.0","Tree Versions":["4.18.2"],"Dependents":["json-server@npm:0.15.1"]}} +{"value":"express","children":{"ID":1100530,"Issue":"express vulnerable to XSS via response.redirect()","URL":"https://github.com/advisories/GHSA-qw6h-vgh9-j6wx","Severity":"low","Vulnerable Versions":"<4.20.0","Tree Versions":["4.18.2"],"Dependents":["json-server@npm:0.15.1"]}} {"value":"figgy-pudding","children":{"ID":"figgy-pudding (deprecation)","Issue":"This module is no longer supported.","Severity":"moderate","Vulnerable Versions":"3.5.2","Tree Versions":["3.5.2"],"Dependents":["npm-registry-fetch@npm:4.0.7"]}} {"value":"fs-write-stream-atomic","children":{"ID":"fs-write-stream-atomic (deprecation)","Issue":"This package is no longer supported.","Severity":"moderate","Vulnerable Versions":"1.0.10","Tree Versions":["1.0.10"],"Dependents":["move-concurrently@npm:1.0.1"]}} {"value":"gauge","children":{"ID":"gauge (deprecation)","Issue":"This package is no longer supported.","Severity":"moderate","Vulnerable Versions":"4.0.4","Tree Versions":["4.0.4"],"Dependents":["npmlog@npm:6.0.2"]}} @@ -26,23 +28,23 @@ {"value":"mermaid","children":{"ID":1100231,"Issue":"Prototype pollution vulnerability found in Mermaid's bundled version of DOMPurify","URL":"https://github.com/advisories/GHSA-m4gq-x24j-jpmf","Severity":"high","Vulnerable Versions":"<=10.9.2","Tree Versions":["10.9.1"],"Dependents":["ngx-markdown@virtual:6ff8c2a3aef81417d9f60600e3255d97c9c6c863d8733a87ed99d869392767523e0e28c07db1eb2a034bc9265813386132447698258584d621a7fd0e13d93585#npm:17.2.1"]}} {"value":"micromatch","children":{"ID":1098681,"Issue":"Regular Expression Denial of Service (ReDoS) in micromatch","URL":"https://github.com/advisories/GHSA-952p-6rrq-rcjv","Severity":"moderate","Vulnerable Versions":"<4.0.8","Tree Versions":["4.0.5"],"Dependents":["fast-glob@npm:3.3.2"]}} {"value":"move-concurrently","children":{"ID":"move-concurrently (deprecation)","Issue":"This package is no longer supported.","Severity":"moderate","Vulnerable Versions":"1.0.1","Tree Versions":["1.0.1"],"Dependents":["cacache@npm:12.0.4"]}} +{"value":"nanoid","children":{"ID":1101092,"Issue":"Infinite loop in nanoid","URL":"https://github.com/advisories/GHSA-mwcw-c2x4-8c55","Severity":"low","Vulnerable Versions":"<3.3.8","Tree Versions":["2.1.11"],"Dependents":["json-server@npm:0.15.1"]}} {"value":"node-fetch-npm","children":{"ID":"node-fetch-npm (deprecation)","Issue":"This module is not used anymore, npm uses minipass-fetch for its fetch implementation now","Severity":"moderate","Vulnerable Versions":"2.0.4","Tree Versions":["2.0.4"],"Dependents":["make-fetch-happen@npm:5.0.2"]}} {"value":"npmlog","children":{"ID":"npmlog (deprecation)","Issue":"This package is no longer supported.","Severity":"moderate","Vulnerable Versions":"6.0.2","Tree Versions":["6.0.2"],"Dependents":["node-gyp@npm:9.4.0"]}} {"value":"osenv","children":{"ID":"osenv (deprecation)","Issue":"This package is no longer supported.","Severity":"moderate","Vulnerable Versions":"0.1.5","Tree Versions":["0.1.5"],"Dependents":["npm-package-arg@npm:6.1.1"]}} {"value":"path-to-regexp","children":{"ID":1099561,"Issue":"path-to-regexp outputs backtracking regular expressions","URL":"https://github.com/advisories/GHSA-9wv6-86v2-598j","Severity":"high","Vulnerable Versions":">=0.2.0 <1.9.0","Tree Versions":["1.8.0"],"Dependents":["express-urlrewrite@npm:1.4.0"]}} {"value":"path-to-regexp","children":{"ID":1099562,"Issue":"path-to-regexp outputs backtracking regular expressions","URL":"https://github.com/advisories/GHSA-9wv6-86v2-598j","Severity":"high","Vulnerable Versions":"<0.1.10","Tree Versions":["0.1.7"],"Dependents":["express@npm:4.18.2"]}} +{"value":"path-to-regexp","children":{"ID":1101081,"Issue":"Unpatched `path-to-regexp` ReDoS in 0.1.x","URL":"https://github.com/advisories/GHSA-rhx6-c78j-4q9w","Severity":"moderate","Vulnerable Versions":"<0.1.12","Tree Versions":["0.1.7"],"Dependents":["express@npm:4.18.2"]}} {"value":"prismjs","children":{"ID":1089189,"Issue":"prismjs Regular Expression Denial of Service vulnerability","URL":"https://github.com/advisories/GHSA-hqhp-5p83-hx96","Severity":"moderate","Vulnerable Versions":"<1.25.0","Tree Versions":["1.24.1"],"Dependents":["@hmcts/ccd-case-ui-toolkit@workspace:."]}} {"value":"prismjs","children":{"ID":1090424,"Issue":"Cross-site Scripting in Prism","URL":"https://github.com/advisories/GHSA-3949-f494-cm99","Severity":"high","Vulnerable Versions":">=1.14.0 <1.27.0","Tree Versions":["1.24.1"],"Dependents":["@hmcts/ccd-case-ui-toolkit@workspace:."]}} {"value":"request","children":{"ID":1096727,"Issue":"Server-Side Request Forgery in Request","URL":"https://github.com/advisories/GHSA-p8p7-x288-28g6","Severity":"moderate","Vulnerable Versions":"<=2.88.2","Tree Versions":["2.88.2"],"Dependents":["json-server@npm:0.15.1"]}} {"value":"resolve-url","children":{"ID":"resolve-url (deprecation)","Issue":"https://github.com/lydell/resolve-url#deprecated","Severity":"moderate","Vulnerable Versions":"0.2.1","Tree Versions":["0.2.1"],"Dependents":["source-map-resolve@npm:0.5.3"]}} -{"value":"rimraf","children":{"ID":"rimraf (deprecation)","Issue":"Rimraf versions prior to v4 are no longer supported","Severity":"moderate","Vulnerable Versions":"3.0.2","Tree Versions":["3.0.2"],"Dependents":["@mapbox/node-pre-gyp@npm:1.0.11"]}} -{"value":"send","children":{"ID":1099525,"Issue":"send vulnerable to template injection that can lead to XSS","URL":"https://github.com/advisories/GHSA-m6fv-jmcg-4jfg","Severity":"moderate","Vulnerable Versions":"<0.19.0","Tree Versions":["0.18.0"],"Dependents":["express@npm:4.18.2"]}} -{"value":"serve-static","children":{"ID":1099527,"Issue":"serve-static vulnerable to template injection that can lead to XSS","URL":"https://github.com/advisories/GHSA-cm22-4g7w-348p","Severity":"moderate","Vulnerable Versions":"<1.16.0","Tree Versions":["1.15.0"],"Dependents":["express@npm:4.18.2"]}} -{"value":"socket.io-parser","children":{"ID":1098329,"Issue":"Insufficient validation when decoding a Socket.IO packet","URL":"https://github.com/advisories/GHSA-cqmj-92xf-r6r9","Severity":"high","Vulnerable Versions":">=4.0.4 <4.2.3","Tree Versions":["4.0.5"],"Dependents":["socket.io-client@npm:3.1.3"]}} +{"value":"rimraf","children":{"ID":"rimraf (deprecation)","Issue":"Rimraf versions prior to v4 are no longer supported","Severity":"moderate","Vulnerable Versions":"3.0.2","Tree Versions":["3.0.2"],"Dependents":["node-gyp@npm:9.4.0"]}} +{"value":"send","children":{"ID":1100526,"Issue":"send vulnerable to template injection that can lead to XSS","URL":"https://github.com/advisories/GHSA-m6fv-jmcg-4jfg","Severity":"low","Vulnerable Versions":"<0.19.0","Tree Versions":["0.18.0"],"Dependents":["express@npm:4.18.2"]}} +{"value":"serve-static","children":{"ID":1100528,"Issue":"serve-static vulnerable to template injection that can lead to XSS","URL":"https://github.com/advisories/GHSA-cm22-4g7w-348p","Severity":"low","Vulnerable Versions":"<1.16.0","Tree Versions":["1.15.0"],"Dependents":["express@npm:4.18.2"]}} {"value":"source-map-resolve","children":{"ID":"source-map-resolve (deprecation)","Issue":"See https://github.com/lydell/source-map-resolve#deprecated","Severity":"moderate","Vulnerable Versions":"0.5.3","Tree Versions":["0.5.3"],"Dependents":["snapdragon@npm:0.8.2"]}} {"value":"source-map-url","children":{"ID":"source-map-url (deprecation)","Issue":"See https://github.com/lydell/source-map-url#deprecated","Severity":"moderate","Vulnerable Versions":"0.4.1","Tree Versions":["0.4.1"],"Dependents":["source-map-resolve@npm:0.5.3"]}} -{"value":"tar","children":{"ID":1097493,"Issue":"Denial of service while parsing a tar file due to lack of folders count validation","URL":"https://github.com/advisories/GHSA-f5x3-32g6-xq36","Severity":"moderate","Vulnerable Versions":"<6.2.1","Tree Versions":["6.1.15"],"Dependents":["@mapbox/node-pre-gyp@npm:1.0.11"]}} +{"value":"tar","children":{"ID":1097493,"Issue":"Denial of service while parsing a tar file due to lack of folders count validation","URL":"https://github.com/advisories/GHSA-f5x3-32g6-xq36","Severity":"moderate","Vulnerable Versions":"<6.2.1","Tree Versions":["6.1.15"],"Dependents":["node-gyp@npm:9.4.0"]}} {"value":"tough-cookie","children":{"ID":1097682,"Issue":"tough-cookie Prototype Pollution vulnerability","URL":"https://github.com/advisories/GHSA-72xf-g2v4-qvf3","Severity":"moderate","Vulnerable Versions":"<4.1.3","Tree Versions":["2.5.0"],"Dependents":["request@npm:2.88.2"]}} {"value":"urix","children":{"ID":"urix (deprecation)","Issue":"Please see https://github.com/lydell/urix#deprecated","Severity":"moderate","Vulnerable Versions":"0.1.0","Tree Versions":["0.1.0"],"Dependents":["source-map-resolve@npm:0.5.3"]}} -{"value":"uuid","children":{"ID":"uuid (deprecation)","Issue":"Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.","Severity":"moderate","Vulnerable Versions":"3.4.0","Tree Versions":["3.4.0"],"Dependents":["@hmcts/media-viewer@virtual:6ff8c2a3aef81417d9f60600e3255d97c9c6c863d8733a87ed99d869392767523e0e28c07db1eb2a034bc9265813386132447698258584d621a7fd0e13d93585#npm:4.0.8"]}} -{"value":"ws","children":{"ID":1098393,"Issue":"ws affected by a DoS when handling a request with many HTTP headers","URL":"https://github.com/advisories/GHSA-3h5v-q93c-6h6q","Severity":"high","Vulnerable Versions":">=7.0.0 <7.5.10","Tree Versions":["7.4.6"],"Dependents":["engine.io-client@npm:4.1.4"]}} +{"value":"uuid","children":{"ID":"uuid (deprecation)","Issue":"Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.","Severity":"moderate","Vulnerable Versions":"3.4.0","Tree Versions":["3.4.0"],"Dependents":["request@npm:2.88.2"]}} diff --git a/yarn.lock b/yarn.lock index a41e01d7ba..b53e6e551d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4331,7 +4331,7 @@ __metadata: "@edium/fsm": "npm:^2.1.2" "@hmcts/ccpay-web-component": "npm:6.2.1" "@hmcts/frontend": "npm:0.0.50-alpha" - "@hmcts/media-viewer": "npm:4.0.8" + "@hmcts/media-viewer": "npm:4.0.9" "@ngrx/effects": "npm:17.2.0" "@ngrx/store": "npm:^17.2.0" "@nicky-lenaers/ngx-scroll-to": "npm:^14.0.0" @@ -4472,16 +4472,16 @@ __metadata: languageName: node linkType: hard -"@hmcts/media-viewer@npm:4.0.8": - version: 4.0.8 - resolution: "@hmcts/media-viewer@npm:4.0.8" +"@hmcts/media-viewer@npm:4.0.9": + version: 4.0.9 + resolution: "@hmcts/media-viewer@npm:4.0.9" dependencies: "@swimlane/ngx-datatable": "npm:^20.0.0" mutable-div: "npm:^2.0.0" - pdfjs-dist: "npm:4.3.136" - socket.io-client: "npm:^3.1.1" + pdfjs-dist: "npm:^4.8.69" + socket.io-client: "npm:^4.8.0" tslib: "npm:^2.3.0" - uuid: "npm:^3.4.0" + uuid: "npm:^11.0.3" peerDependencies: "@angular/animations": ^17.3.8 "@angular/common": ^17.3.8 @@ -4493,7 +4493,7 @@ __metadata: "@ngrx/router-store": ^17.0.0 "@ngrx/store": ^17.0.0 "@ngrx/store-devtools": ^17.0.0 - checksum: 10/f2b97954a59e89ff8559933e9dc24defb5f2879644d5bf353b16c5743db58449d446703bac6f551bb6ef39b7a0f772ca4dd21d70cc816cd492e6fdb43d5919bf + checksum: 10/2366868102b9b06f15c1dfc96ffcd0cfd72e51c795c492566d3e438e8b5a80408b5e87586b93fe5331f308eddc5cfab88ac8b8bd9ed69da965ea456557ba4152 languageName: node linkType: hard @@ -4774,25 +4774,6 @@ __metadata: languageName: node linkType: hard -"@mapbox/node-pre-gyp@npm:^1.0.0": - version: 1.0.11 - resolution: "@mapbox/node-pre-gyp@npm:1.0.11" - dependencies: - detect-libc: "npm:^2.0.0" - https-proxy-agent: "npm:^5.0.0" - make-dir: "npm:^3.1.0" - node-fetch: "npm:^2.6.7" - nopt: "npm:^5.0.0" - npmlog: "npm:^5.0.1" - rimraf: "npm:^3.0.2" - semver: "npm:^7.3.5" - tar: "npm:^6.1.11" - bin: - node-pre-gyp: bin/node-pre-gyp - checksum: 10/59529a2444e44fddb63057152452b00705aa58059079191126c79ac1388ae4565625afa84ed4dd1bf017d1111ab6e47907f7c5192e06d83c9496f2f3e708680a - languageName: node - linkType: hard - "@material/animation@npm:15.0.0-canary.bc9ae6c9c.0": version: 15.0.0-canary.bc9ae6c9c.0 resolution: "@material/animation@npm:15.0.0-canary.bc9ae6c9c.0" @@ -5648,6 +5629,115 @@ __metadata: languageName: node linkType: hard +"@napi-rs/canvas-android-arm64@npm:0.1.65": + version: 0.1.65 + resolution: "@napi-rs/canvas-android-arm64@npm:0.1.65" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/canvas-darwin-arm64@npm:0.1.65": + version: 0.1.65 + resolution: "@napi-rs/canvas-darwin-arm64@npm:0.1.65" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/canvas-darwin-x64@npm:0.1.65": + version: 0.1.65 + resolution: "@napi-rs/canvas-darwin-x64@npm:0.1.65" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/canvas-linux-arm-gnueabihf@npm:0.1.65": + version: 0.1.65 + resolution: "@napi-rs/canvas-linux-arm-gnueabihf@npm:0.1.65" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@napi-rs/canvas-linux-arm64-gnu@npm:0.1.65": + version: 0.1.65 + resolution: "@napi-rs/canvas-linux-arm64-gnu@npm:0.1.65" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/canvas-linux-arm64-musl@npm:0.1.65": + version: 0.1.65 + resolution: "@napi-rs/canvas-linux-arm64-musl@npm:0.1.65" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@napi-rs/canvas-linux-riscv64-gnu@npm:0.1.65": + version: 0.1.65 + resolution: "@napi-rs/canvas-linux-riscv64-gnu@npm:0.1.65" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/canvas-linux-x64-gnu@npm:0.1.65": + version: 0.1.65 + resolution: "@napi-rs/canvas-linux-x64-gnu@npm:0.1.65" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/canvas-linux-x64-musl@npm:0.1.65": + version: 0.1.65 + resolution: "@napi-rs/canvas-linux-x64-musl@npm:0.1.65" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@napi-rs/canvas-win32-x64-msvc@npm:0.1.65": + version: 0.1.65 + resolution: "@napi-rs/canvas-win32-x64-msvc@npm:0.1.65" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/canvas@npm:^0.1.64": + version: 0.1.65 + resolution: "@napi-rs/canvas@npm:0.1.65" + dependencies: + "@napi-rs/canvas-android-arm64": "npm:0.1.65" + "@napi-rs/canvas-darwin-arm64": "npm:0.1.65" + "@napi-rs/canvas-darwin-x64": "npm:0.1.65" + "@napi-rs/canvas-linux-arm-gnueabihf": "npm:0.1.65" + "@napi-rs/canvas-linux-arm64-gnu": "npm:0.1.65" + "@napi-rs/canvas-linux-arm64-musl": "npm:0.1.65" + "@napi-rs/canvas-linux-riscv64-gnu": "npm:0.1.65" + "@napi-rs/canvas-linux-x64-gnu": "npm:0.1.65" + "@napi-rs/canvas-linux-x64-musl": "npm:0.1.65" + "@napi-rs/canvas-win32-x64-msvc": "npm:0.1.65" + dependenciesMeta: + "@napi-rs/canvas-android-arm64": + optional: true + "@napi-rs/canvas-darwin-arm64": + optional: true + "@napi-rs/canvas-darwin-x64": + optional: true + "@napi-rs/canvas-linux-arm-gnueabihf": + optional: true + "@napi-rs/canvas-linux-arm64-gnu": + optional: true + "@napi-rs/canvas-linux-arm64-musl": + optional: true + "@napi-rs/canvas-linux-riscv64-gnu": + optional: true + "@napi-rs/canvas-linux-x64-gnu": + optional: true + "@napi-rs/canvas-linux-x64-musl": + optional: true + "@napi-rs/canvas-win32-x64-msvc": + optional: true + checksum: 10/71ab1c29a9f15bd5ca0406dced5e3e939b6645b1eb79e3c26cd17695b41db61d6b3640cd54460b667abf234dab0b0217eb792900459a75d9f95f9d76e4f83b54 + languageName: node + linkType: hard + "@ngrx/effects@npm:17.2.0": version: 17.2.0 resolution: "@ngrx/effects@npm:17.2.0" @@ -7619,13 +7709,6 @@ __metadata: languageName: node linkType: hard -"@types/component-emitter@npm:^1.2.10": - version: 1.2.11 - resolution: "@types/component-emitter@npm:1.2.11" - checksum: 10/0e081c5f7a4b113af3732f67ad9ebb487d5c239d440d96938ff9a679d18bb9337a513638e12b5b02a7a921494eef18c5a4d78f1188bc43a12290edd74c42a9c7 - languageName: node - linkType: hard - "@types/connect-history-api-fallback@npm:^1.3.5": version: 1.5.4 resolution: "@types/connect-history-api-fallback@npm:1.5.4" @@ -10528,13 +10611,6 @@ __metadata: languageName: node linkType: hard -"backo2@npm:~1.0.2": - version: 1.0.2 - resolution: "backo2@npm:1.0.2" - checksum: 10/fda8d0a0f4810068d23715f2f45153146d6ee8f62dd827ce1e0b6cc3c8328e84ad61e11399a83931705cef702fe7cbb457856bf99b9bd10c4ed57b0786252385 - languageName: node - linkType: hard - "bail@npm:^1.0.0": version: 1.0.5 resolution: "bail@npm:1.0.5" @@ -10549,13 +10625,6 @@ __metadata: languageName: node linkType: hard -"base64-arraybuffer@npm:0.1.4": - version: 0.1.4 - resolution: "base64-arraybuffer@npm:0.1.4" - checksum: 10/25c81d85a018fa4e19c311642f5d3667a4b5ee56b4fd8bc7499f95f28166a7ee2b552549887bdf577154522164e10e06a307d276293aa240a9e6d3ebf16de54d - languageName: node - linkType: hard - "base64-js@npm:1.3.1": version: 1.3.1 resolution: "base64-js@npm:1.3.1" @@ -11385,18 +11454,6 @@ __metadata: languageName: node linkType: hard -"canvas@npm:^2.11.2": - version: 2.11.2 - resolution: "canvas@npm:2.11.2" - dependencies: - "@mapbox/node-pre-gyp": "npm:^1.0.0" - nan: "npm:^2.17.0" - node-gyp: "npm:latest" - simple-get: "npm:^3.0.3" - checksum: 10/500040e93310b33f5733746b909712fdeced56aa74a1370c563f0c7ffc5b4a31006b2d881644b59eea8ab6c465406705a59b280f1d4e3ec4e1e2c88d4a725ca6 - languageName: node - linkType: hard - "capture-exit@npm:^2.0.0": version: 2.0.0 resolution: "capture-exit@npm:2.0.0" @@ -12153,7 +12210,7 @@ __metadata: languageName: node linkType: hard -"component-emitter@npm:^1.2.1, component-emitter@npm:~1.3.0": +"component-emitter@npm:^1.2.1": version: 1.3.0 resolution: "component-emitter@npm:1.3.0" checksum: 10/dfc1ec2e7aa2486346c068f8d764e3eefe2e1ca0b24f57506cd93b2ae3d67829a7ebd7cc16e2bf51368fac2f45f78fcff231718e40b1975647e4a86be65e1d05 @@ -13445,15 +13502,6 @@ __metadata: languageName: node linkType: hard -"decompress-response@npm:^4.2.0": - version: 4.2.1 - resolution: "decompress-response@npm:4.2.1" - dependencies: - mimic-response: "npm:^2.0.0" - checksum: 10/4e783ca4dfe9417354d61349750fe05236f565a4415a6ca20983a311be2371debaedd9104c0b0e7b36e5f167aeaae04f84f1a0b3f8be4162f1d7d15598b8fdba - languageName: node - linkType: hard - "deep-equal@npm:^1.0.0": version: 1.1.1 resolution: "deep-equal@npm:1.1.1" @@ -13748,13 +13796,6 @@ __metadata: languageName: node linkType: hard -"detect-libc@npm:^2.0.0": - version: 2.0.3 - resolution: "detect-libc@npm:2.0.3" - checksum: 10/b4ea018d623e077bd395f168a9e81db77370dde36a5b01d067f2ad7989924a81d31cb547ff764acb2aa25d50bb7fdde0b0a93bec02212b0cb430621623246d39 - languageName: node - linkType: hard - "detect-node@npm:^2.0.4": version: 2.1.0 resolution: "detect-node@npm:2.1.0" @@ -14276,30 +14317,16 @@ __metadata: languageName: node linkType: hard -"engine.io-client@npm:~4.1.0": - version: 4.1.4 - resolution: "engine.io-client@npm:4.1.4" +"engine.io-client@npm:~6.6.1": + version: 6.6.2 + resolution: "engine.io-client@npm:6.6.2" dependencies: - base64-arraybuffer: "npm:0.1.4" - component-emitter: "npm:~1.3.0" + "@socket.io/component-emitter": "npm:~3.1.0" debug: "npm:~4.3.1" - engine.io-parser: "npm:~4.0.1" - has-cors: "npm:1.1.0" - parseqs: "npm:0.0.6" - parseuri: "npm:0.0.6" - ws: "npm:~7.4.2" - xmlhttprequest-ssl: "npm:~1.6.2" - yeast: "npm:0.1.2" - checksum: 10/7cb4a09c5170266d76f827c069c51a0ff07a733d426c21549f6b90b7718a2210b356327daff488f64ffeaceb3169606372455f8e3770e183717b63d2467dd418 - languageName: node - linkType: hard - -"engine.io-parser@npm:~4.0.1": - version: 4.0.3 - resolution: "engine.io-parser@npm:4.0.3" - dependencies: - base64-arraybuffer: "npm:0.1.4" - checksum: 10/5b1d2a3e3269164a879e626d086e34b8b37b09b0133b2dfb9df2234fed45bab1f48822236f620518e76a898018e9827196ee9e0f6f8f83f56182a8748eb36c4b + engine.io-parser: "npm:~5.2.1" + ws: "npm:~8.17.1" + xmlhttprequest-ssl: "npm:~2.1.1" + checksum: 10/c006b3389bb8bd0381926b9633e9f547dec187ea28d2dd99cb42d516b0720bc4373f3f937c199ca616c95b2832e0f547f73326f614caedfe39c02fa93b7ac733 languageName: node linkType: hard @@ -14310,6 +14337,13 @@ __metadata: languageName: node linkType: hard +"engine.io-parser@npm:~5.2.1": + version: 5.2.3 + resolution: "engine.io-parser@npm:5.2.3" + checksum: 10/eb0023fff5766e7ae9d59e52d92df53fea06d472cfd7b52e5d2c36b4c1dbf78cab5fde1052bcb3d4bb85bdb5aee10ae85d8a1c6c04676dac0c6cdf16bcba6380 + languageName: node + linkType: hard + "engine.io@npm:~6.5.0": version: 6.5.1 resolution: "engine.io@npm:6.5.1" @@ -17359,13 +17393,6 @@ __metadata: languageName: node linkType: hard -"has-cors@npm:1.1.0": - version: 1.1.0 - resolution: "has-cors@npm:1.1.0" - checksum: 10/549ce94113fd23895b22d71ade9809918577b8558cd4d701fe79045d8b1d58d87eba870260b28f6a3229be933a691c55653afd496d0fc52e98fd2ff577f01197 - languageName: node - linkType: hard - "has-flag@npm:^1.0.0": version: 1.0.0 resolution: "has-flag@npm:1.0.0" @@ -21903,13 +21930,6 @@ __metadata: languageName: node linkType: hard -"mimic-response@npm:^2.0.0": - version: 2.1.0 - resolution: "mimic-response@npm:2.1.0" - checksum: 10/014fad6ab936657e5f2f48bd87af62a8e928ebe84472aaf9e14fec4fcb31257a5edff77324d8ac13ddc6685ba5135cf16e381efac324e5f174fb4ddbf902bf07 - languageName: node - linkType: hard - "min-document@npm:^2.19.0": version: 2.19.0 resolution: "min-document@npm:2.19.0" @@ -22310,15 +22330,6 @@ __metadata: languageName: node linkType: hard -"nan@npm:^2.17.0": - version: 2.20.0 - resolution: "nan@npm:2.20.0" - dependencies: - node-gyp: "npm:latest" - checksum: 10/5f16e4c9953075d9920229c703c1d781c0b74118ce3d9e926b448a4eef92b7d8be5ac6adc748a13a5fafb594436cbfe63250e3471aefdd78e3a0cd14603b9ba7 - languageName: node - linkType: hard - "nanoid@npm:3.1.20": version: 3.1.20 resolution: "nanoid@npm:3.1.20" @@ -22758,17 +22769,6 @@ __metadata: languageName: node linkType: hard -"nopt@npm:^5.0.0": - version: 5.0.0 - resolution: "nopt@npm:5.0.0" - dependencies: - abbrev: "npm:1" - bin: - nopt: bin/nopt.js - checksum: 10/00f9bb2d16449469ba8ffcf9b8f0eae6bae285ec74b135fec533e5883563d2400c0cd70902d0a7759e47ac031ccf206ace4e86556da08ed3f1c66dda206e9ccd - languageName: node - linkType: hard - "nopt@npm:^6.0.0": version: 6.0.0 resolution: "nopt@npm:6.0.0" @@ -23962,20 +23962,6 @@ __metadata: languageName: node linkType: hard -"parseqs@npm:0.0.6": - version: 0.0.6 - resolution: "parseqs@npm:0.0.6" - checksum: 10/7fc4ff4ba59764060bb8529875f6d4313056ea6939ff579b22dd7bd6f6033035e1fd2d6a559ab48ef0a7fa29a9d7731c982bfd1594e9115141fe1c328485ce9e - languageName: node - linkType: hard - -"parseuri@npm:0.0.6": - version: 0.0.6 - resolution: "parseuri@npm:0.0.6" - checksum: 10/fa430e40f0c75293a28e5f1023da5f51a5038d5e34c48c517b0d5187143f6bcc67d3091a062b68765db4a22757e488c7d15854f9d1921f2c2b9afa5ca0629a84 - languageName: node - linkType: hard - "parseurl@npm:~1.3.2, parseurl@npm:~1.3.3": version: 1.3.3 resolution: "parseurl@npm:1.3.3" @@ -24158,13 +24144,6 @@ __metadata: languageName: node linkType: hard -"path2d@npm:^0.2.0": - version: 0.2.1 - resolution: "path2d@npm:0.2.1" - checksum: 10/16dd250206069417cc9e7377a8f077a4f385e1b0ed121d9766420f592b5519fad8c160b85205561fcdd8cf5169c779deb9e2e883ed61b21980c9201c6df198f8 - languageName: node - linkType: hard - "pause-stream@npm:0.0.11": version: 0.0.11 resolution: "pause-stream@npm:0.0.11" @@ -24187,18 +24166,15 @@ __metadata: languageName: node linkType: hard -"pdfjs-dist@npm:4.3.136": - version: 4.3.136 - resolution: "pdfjs-dist@npm:4.3.136" +"pdfjs-dist@npm:^4.8.69": + version: 4.9.155 + resolution: "pdfjs-dist@npm:4.9.155" dependencies: - canvas: "npm:^2.11.2" - path2d: "npm:^0.2.0" + "@napi-rs/canvas": "npm:^0.1.64" dependenciesMeta: - canvas: - optional: true - path2d: + "@napi-rs/canvas": optional: true - checksum: 10/5511a54a0811c93c6d0517d3bd7ee1df5ffc00577d8b27054956d3775bb9a8a75427ad878c4b13fdda830aabf17e2807667b957e499848bb5968caaba4254d46 + checksum: 10/0eccb8d8a29b93c088055c80061e905c5d87768faf4c24f5d32f4a22dd9847e07aaffda0658f34959f04b895818fb6c9c81b5161cb79ce8fec8248b2ec4dd672 languageName: node linkType: hard @@ -26983,24 +26959,6 @@ __metadata: languageName: node linkType: hard -"simple-concat@npm:^1.0.0": - version: 1.0.1 - resolution: "simple-concat@npm:1.0.1" - checksum: 10/4d211042cc3d73a718c21ac6c4e7d7a0363e184be6a5ad25c8a1502e49df6d0a0253979e3d50dbdd3f60ef6c6c58d756b5d66ac1e05cda9cacd2e9fc59e3876a - languageName: node - linkType: hard - -"simple-get@npm:^3.0.3": - version: 3.1.1 - resolution: "simple-get@npm:3.1.1" - dependencies: - decompress-response: "npm:^4.2.0" - once: "npm:^1.3.1" - simple-concat: "npm:^1.0.0" - checksum: 10/94fa04e74077c2607142f7597af8409c6c8d1e9487b597ce1da6f824e732b3e51ef492e495a4d8a2a12a94780214d77a8d3bb81c2139b3ec4ce21b93224442c0 - languageName: node - linkType: hard - "sisteransi@npm:^1.0.5": version: 1.0.5 resolution: "sisteransi@npm:1.0.5" @@ -27098,29 +27056,15 @@ __metadata: languageName: node linkType: hard -"socket.io-client@npm:^3.1.1": - version: 3.1.3 - resolution: "socket.io-client@npm:3.1.3" - dependencies: - "@types/component-emitter": "npm:^1.2.10" - backo2: "npm:~1.0.2" - component-emitter: "npm:~1.3.0" - debug: "npm:~4.3.1" - engine.io-client: "npm:~4.1.0" - parseuri: "npm:0.0.6" - socket.io-parser: "npm:~4.0.4" - checksum: 10/96165ddf6c60b30cbfcea9a3b072db6d2dae44886abe16b330af9d14ceba28059cbc738c38232984ebc34293fea005789d54748204d5fe6a04e45763dcae22a0 - languageName: node - linkType: hard - -"socket.io-parser@npm:~4.0.4": - version: 4.0.5 - resolution: "socket.io-parser@npm:4.0.5" +"socket.io-client@npm:^4.8.0": + version: 4.8.1 + resolution: "socket.io-client@npm:4.8.1" dependencies: - "@types/component-emitter": "npm:^1.2.10" - component-emitter: "npm:~1.3.0" - debug: "npm:~4.3.1" - checksum: 10/8985f6c578d9a719418d67df74f78a3e2b318b2dbc7087c03b2592927b0050b8e71913e6968bb50924030aeb40b5bd692e099f197188efb1824aa873ed6fbbb9 + "@socket.io/component-emitter": "npm:~3.1.0" + debug: "npm:~4.3.2" + engine.io-client: "npm:~6.6.1" + socket.io-parser: "npm:~4.2.4" + checksum: 10/7480cf1ab30eba371a96dd1ce2ce9018dcbeaf81035a066fb89d99df0d0a6388b05840c92d970317c739956b68b28b0f4833f3b18e460a24eef557b9bca127c1 languageName: node linkType: hard @@ -29701,7 +29645,16 @@ __metadata: languageName: node linkType: hard -"uuid@npm:^3.0.0, uuid@npm:^3.3.2, uuid@npm:^3.4.0": +"uuid@npm:^11.0.3": + version: 11.0.3 + resolution: "uuid@npm:11.0.3" + bin: + uuid: dist/esm/bin/uuid + checksum: 10/251385563195709eb0697c74a834764eef28e1656d61174e35edbd129288acb4d95a43f4ce8a77b8c2fc128e2b55924296a0945f964b05b9173469d045625ff2 + languageName: node + linkType: hard + +"uuid@npm:^3.0.0, uuid@npm:^3.3.2": version: 3.4.0 resolution: "uuid@npm:3.4.0" bin: @@ -30810,7 +30763,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.11.0, ws@npm:^8.13.0": +"ws@npm:^8.11.0, ws@npm:^8.13.0, ws@npm:~8.17.1": version: 8.17.1 resolution: "ws@npm:8.17.1" peerDependencies: @@ -30840,21 +30793,6 @@ __metadata: languageName: node linkType: hard -"ws@npm:~7.4.2": - version: 7.4.6 - resolution: "ws@npm:7.4.6" - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - checksum: 10/150e3f917b7cde568d833a5ea6ccc4132e59c38d04218afcf2b6c7b845752bd011a9e0dc1303c8694d3c402a0bdec5893661a390b71ff88f0fc81a4e4e66b09c - languageName: node - linkType: hard - "ws@npm:~8.11.0": version: 8.11.0 resolution: "ws@npm:8.11.0" @@ -31089,13 +31027,6 @@ __metadata: languageName: node linkType: hard -"yeast@npm:0.1.2": - version: 0.1.2 - resolution: "yeast@npm:0.1.2" - checksum: 10/5536b5cfb3fc4ddf0c9eb4d2834a3a87e094d267bc40a2713d5b87a8da47c8615b8ffe475f3f92a085628e46be4704917ac0fea4827b3e8d27ac18b4230b6819 - languageName: node - linkType: hard - "yn@npm:3.1.1": version: 3.1.1 resolution: "yn@npm:3.1.1" From b6f023b31f1f80582d1e379a96533238e97d48a3 Mon Sep 17 00:00:00 2001 From: Olu <142989683+olusegz07@users.noreply.github.com> Date: Mon, 16 Dec 2024 10:59:07 +0000 Subject: [PATCH 2/3] Feature/query management (#1818) * Copy update (#1817) * update response status (#1816) * Update yarn-audit-known-issues * Update case-header.component.ts (#1814) * Update query-case-details-header.component.ts * Update yarn-audit-known-issues * update toolkit version * code tidy and fix sonar cloud issue * code tidy * version update * Remove copy and link (#1820) * version update * version update * version toolkit * Update qualifying-question-options.component.spec.ts * Code tidy * release version --- package.json | 2 +- projects/ccd-case-ui-toolkit/package.json | 2 +- .../services/page-validation.service.ts | 15 ++++---- .../case-header/case-header.component.ts | 16 ++++++--- .../select-flag-type.component.ts | 18 ---------- ...qualifying-question-options.component.html | 4 --- ...lifying-question-options.component.spec.ts | 12 ++++++- .../qualifying-question-options.component.ts | 2 +- .../query-case-details-header.component.ts | 23 +++--------- .../enums/query-item-response-status.enum.ts | 3 +- .../query-list-item.model.spec.ts | 36 +++++++++++++++++-- .../query-list-item/query-list-item.model.ts | 7 +++- .../jurisdiction/jurisdiction.service.ts | 1 - 13 files changed, 79 insertions(+), 62 deletions(-) diff --git a/package.json b/package.json index c4bcc203d2..6327a05e03 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@hmcts/ccd-case-ui-toolkit", - "version": "7.1.15-fix-event-hiding", + "version": "7.1.22", "engines": { "node": ">=18.19.0" }, diff --git a/projects/ccd-case-ui-toolkit/package.json b/projects/ccd-case-ui-toolkit/package.json index 5afc666adf..350cda0a43 100644 --- a/projects/ccd-case-ui-toolkit/package.json +++ b/projects/ccd-case-ui-toolkit/package.json @@ -1,6 +1,6 @@ { "name": "@hmcts/ccd-case-ui-toolkit", - "version": "7.1.15-fix-event-hiding", + "version": "7.1.22", "engines": { "node": ">=18.19.0" }, diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/services/page-validation.service.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/services/page-validation.service.ts index 172d7443ec..d8f5593492 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/services/page-validation.service.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/services/page-validation.service.ts @@ -14,15 +14,15 @@ export class PageValidationService { public getInvalidFields(page: WizardPage, editForm: FormGroup): CaseField[] { const failingCaseFields = []; page.case_fields - .filter(caseField => !this.caseFieldService.isReadOnly(caseField)) - .filter(caseField => !this.isHidden(caseField, editForm)) - .forEach(caseField => { + .filter((caseField) => !this.caseFieldService.isReadOnly(caseField)) + .filter((caseField) => !this.isHidden(caseField, editForm)) + .forEach((caseField) => { const theControl = FieldsUtils.isCaseFieldOfType(caseField, ['JudicialUser']) - ? editForm.controls['data'].get(`${caseField.id}_judicialUserControl`) - : editForm.controls['data'].get(caseField.id); + ? editForm.controls.data.get(`${caseField.id}_judicialUserControl`) + : editForm.controls.data.get(caseField.id); if (!(this.checkDocumentField(caseField, theControl) && this.checkOptionalField(caseField, theControl))) { failingCaseFields.push(caseField); - }; + } }); return failingCaseFields; } @@ -46,9 +46,8 @@ export class PageValidationService { private checkOptionalField(caseField: CaseField, theControl: AbstractControl): boolean { if (!theControl) { return this.caseFieldService.isOptional(caseField); - } else { - return theControl.valid || theControl.disabled; } + return theControl.valid || theControl.disabled; } private checkMandatoryField(caseField: CaseField, theControl: AbstractControl): boolean { diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-header/case-header.component.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-header/case-header.component.ts index ddbb437155..7c38d0f90a 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-header/case-header.component.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-header/case-header.component.ts @@ -10,17 +10,25 @@ import { Draft } from '../../domain/draft.model'; }) export class CaseHeaderComponent implements OnInit { - @Input() public caseDetails: CaseView; + + @Input() + public caseDetails: CaseView; public caseTitle: CaseField; public caseFields: CaseField[]; public ngOnInit(): void { this.caseTitle = new CaseField(); - this.caseTitle.label = this.caseDetails.state.title_display; - this.caseFields = this.getCaseFieldsInfo(); + if (!this.isDraft() && this.caseDetails.state.title_display) { + this.caseTitle.label = this.caseDetails.state.title_display; + this.caseFields = this.getCaseFields(); + } + } + + public isDraft(): boolean { + return Draft.isDraft(this.caseDetails.case_id); } - private getCaseFieldsInfo(): CaseField[] { + private getCaseFields(): CaseField[] { const caseDataFields = this.caseDetails.tabs.reduce((acc, tab) => { return acc.concat(tab.fields); }, []); diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/case-flag/components/select-flag-type/select-flag-type.component.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/case-flag/components/select-flag-type/select-flag-type.component.ts index a7215ad5bf..ad077e38e8 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/case-flag/components/select-flag-type/select-flag-type.component.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/case-flag/components/select-flag-type/select-flag-type.component.ts @@ -98,24 +98,6 @@ export class SelectFlagTypeComponent implements OnInit, OnDestroy { } ); - this.formGroup.addControl(CaseFlagFormFields.FLAG_TYPE, new FormControl('')); - this.formGroup.addControl(CaseFlagFormFields.OTHER_FLAG_DESCRIPTION, new FormControl('')); - this.formGroup.addControl(CaseFlagFormFields.IS_VISIBLE_INTERNALLY_ONLY, new FormControl('')); - - // Should clear descriptionControlName if flagTypeControlName is changed - this.flagTypeControlChangesSubscription = this.formGroup.get(CaseFlagFormFields.FLAG_TYPE).valueChanges - .subscribe(_ => { - this.formGroup.get(CaseFlagFormFields.OTHER_FLAG_DESCRIPTION).setValue(''); - this.cachedPath = []; - - // required to clear language interpreter - this.formGroup.patchValue({ - [SearchLanguageInterpreterControlNames.LANGUAGE_SEARCH_TERM]: '', - [SearchLanguageInterpreterControlNames.MANUAL_LANGUAGE_ENTRY]: '' - }); - } - ); - // If hmctsServiceId is present, use this to retrieve the relevant list of flag types if (this.hmctsServiceId) { this.flagRefdata$ = this.caseFlagRefdataService diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/components/qualifying-questions/qualifying-question-options/qualifying-question-options.component.html b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/components/qualifying-questions/qualifying-question-options/qualifying-question-options.component.html index 50eaef8432..ab76b7d823 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/components/qualifying-questions/qualifying-question-options/qualifying-question-options.component.html +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/components/qualifying-questions/qualifying-question-options/qualifying-question-options.component.html @@ -27,10 +27,6 @@

type="radio" [formControl]="qualifyingQuestionsControl" [value]="qualifyingQuestion"> -
- {{ '(To follow-up on an existing query' | rpxTranslate }} - {{ 'click here' | rpxTranslate }}.) -
diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/components/qualifying-questions/qualifying-question-options/qualifying-question-options.component.spec.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/components/qualifying-questions/qualifying-question-options/qualifying-question-options.component.spec.ts index 50d3f0acd5..7a6ae94634 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/components/qualifying-questions/qualifying-question-options/qualifying-question-options.component.spec.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/components/qualifying-questions/qualifying-question-options/qualifying-question-options.component.spec.ts @@ -66,7 +66,7 @@ describe('QualifyingQuestionOptionsComponent', () => { it('should have the link to case details queries tab', () => { component.click(); - expect(router.navigate).toHaveBeenCalledWith(['cases', 'case-details', '12345'], { fragment: 'Query Management' }); + expect(router.navigate).toHaveBeenCalledWith(['cases', 'case-details', '12345'], { fragment: 'Queries' }); }); describe('displayError', () => { @@ -109,5 +109,15 @@ describe('QualifyingQuestionOptionsComponent', () => { expect(errorMessageEl).toBeTruthy(); expect(errorMessageEl.nativeElement.textContent.trim()).toBe(`Error: ${QualifyingQuestionsErrorMessage.SELECT_AN_OPTION}`); }); + + it('should initialize qualifyingQuestionsControl with saved selection if available', () => { + const savedSelection = 'saved-option'; + qualifyingQuestionService.getQualifyingQuestionSelection.and.returnValue(savedSelection); + + component.ngOnInit(); + + expect(qualifyingQuestionService.getQualifyingQuestionSelection).toHaveBeenCalled(); + expect(component.qualifyingQuestionsControl.value).toBe(savedSelection); + }); }); }); diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/components/qualifying-questions/qualifying-question-options/qualifying-question-options.component.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/components/qualifying-questions/qualifying-question-options/qualifying-question-options.component.ts index f809a37a50..765a6115be 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/components/qualifying-questions/qualifying-question-options/qualifying-question-options.component.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/components/qualifying-questions/qualifying-question-options/qualifying-question-options.component.ts @@ -33,7 +33,7 @@ export class QualifyingQuestionOptionsComponent implements OnInit { } public click(): void { - this.router.navigate(['cases', 'case-details', this.caseId], { fragment: 'Query Management' }); + this.router.navigate(['cases', 'case-details', this.caseId], { fragment: 'Queries' }); } public get displayError(): boolean { diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/components/query-case-details-header/query-case-details-header.component.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/components/query-case-details-header/query-case-details-header.component.ts index e923fddb14..b664b3738a 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/components/query-case-details-header/query-case-details-header.component.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/components/query-case-details-header/query-case-details-header.component.ts @@ -1,37 +1,24 @@ import { Component, Input, OnInit } from '@angular/core'; import { CaseView } from '../../../../../domain/case-view/case-view.model'; import { CaseField } from '../../../../../domain/definition/case-field.model'; -import { Draft } from '../../../../../domain/draft.model'; -import { ActivatedRoute } from '@angular/router'; @Component({ selector: 'ccd-query-case-details-header', templateUrl: './query-case-details-header.component.html' }) + export class QueryCaseDetailsHeaderComponent implements OnInit { - @Input() - public caseDetails: CaseView; + @Input() public caseDetails: CaseView; public caseTitle: CaseField; public caseFields: CaseField[]; - public caseView: CaseView; - - constructor(activatedRoute: ActivatedRoute) { - this.caseView = activatedRoute.snapshot.data.case; - } public ngOnInit(): void { this.caseTitle = new CaseField(); - if (!this.isDraft() && this.caseDetails.state.title_display) { - this.caseTitle.label = this.caseDetails.state.title_display; - this.caseFields = this.getCaseFields(); - } - } - - public isDraft(): boolean { - return Draft.isDraft(this.caseDetails.case_id); + this.caseTitle.label = this.caseDetails.state.title_display; + this.caseFields = this.getCaseFieldsInfo(); } - private getCaseFields(): CaseField[] { + private getCaseFieldsInfo(): CaseField[] { const caseDataFields = this.caseDetails.tabs.reduce((acc, tab) => { return acc.concat(tab.fields); }, []); diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/enums/query-item-response-status.enum.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/enums/query-item-response-status.enum.ts index 7cf440f594..5d37a728fa 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/enums/query-item-response-status.enum.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/enums/query-item-response-status.enum.ts @@ -1,4 +1,5 @@ export enum QueryItemResponseStatus { NEW = 'New', - RESPONDED = 'Responded' + RESPONDED = 'Responded', + AWAITING = 'Awaiting Response' } diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/models/query-list/query-list-item/query-list-item.model.spec.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/models/query-list/query-list-item/query-list-item.model.spec.ts index e52e9b2a04..8241ce41d7 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/models/query-list/query-list-item/query-list-item.model.spec.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/models/query-list/query-list-item/query-list-item.model.spec.ts @@ -56,11 +56,11 @@ describe('QueryListItem', () => { hearingDate: '', createdOn: new Date('2023-06-01'), createdBy: 'Person D', - parentId: '444-444', + parentId: '444-444' } ]; - const childrenItems = items.map(item => { + const childrenItems = items.map((item) => { const listItem = new QueryListItem(); Object.assign(listItem, item); return listItem; @@ -163,7 +163,37 @@ describe('QueryListItem', () => { it('should return "No response required" when it has no children', () => { queryListItem.children = []; expect(queryListItem.children).toEqual([]); - expect(queryListItem.responseStatus).toEqual(QueryItemResponseStatus.NEW); + expect(queryListItem.responseStatus).toEqual(QueryItemResponseStatus.AWAITING); + }); + + it('should return "Awaiting Response" when it has children and is for Follow up question', () => { + // Create additional child items + const additionalChildren = [ + new QueryListItem(), + new QueryListItem(), + new QueryListItem(), + new QueryListItem() + ].map((child, index) => { + Object.assign(child, { + id: `child-${index + 1}`, + subject: `Subject ${index + 2}`, + name: `Name ${index + 2}`, + body: `Body ${index + 2}`, + attachments: [], + isHearingRelated: false, + hearingDate: '', + createdOn: new Date(`2021-0${index + 2}-01`), + createdBy: `Person ${String.fromCharCode(65 + index + 1)}`, + parentId: queryListItem.id, + children: [] + }); + return child; + }); + + // Assign these new children to queryListItem + queryListItem.children = additionalChildren; + expect(queryListItem.children.length).toEqual(4); + expect(queryListItem.responseStatus).toEqual(QueryItemResponseStatus.AWAITING); }); }); }); diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/models/query-list/query-list-item/query-list-item.model.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/models/query-list/query-list-item/query-list-item.model.ts index 57c54dd275..739adbc1c6 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/models/query-list/query-list-item/query-list-item.model.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/query-management/models/query-list/query-list-item/query-list-item.model.ts @@ -50,6 +50,11 @@ export class QueryListItem implements CaseMessage { } public get responseStatus(): QueryItemResponseStatus { - return this.children?.length > 0 ? QueryItemResponseStatus.RESPONDED : QueryItemResponseStatus.NEW; + if (this.children?.length > 0) { + return this.children.length % 2 === 1 + ? QueryItemResponseStatus.RESPONDED + : QueryItemResponseStatus.AWAITING; + } + return QueryItemResponseStatus.AWAITING; } } diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/services/jurisdiction/jurisdiction.service.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/services/jurisdiction/jurisdiction.service.ts index 8955ea21be..158695b50d 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/services/jurisdiction/jurisdiction.service.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/services/jurisdiction/jurisdiction.service.ts @@ -20,7 +20,6 @@ export class JurisdictionService { } public announceSelectedJurisdiction(jurisdiction: Jurisdiction): void { - console.info ('Announcing selected jurisdiction = ' + jurisdiction?.id); this.selectedJurisdictionSource.next(jurisdiction); this.selectedJurisdictionBS.next(jurisdiction); } From ada884f887c2292d75f09dd88deb093d50bb75b8 Mon Sep 17 00:00:00 2001 From: Olu <142989683+olusegz07@users.noreply.github.com> Date: Mon, 16 Dec 2024 11:42:41 +0000 Subject: [PATCH 3/3] version update (#1832) --- package.json | 2 +- projects/ccd-case-ui-toolkit/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 6327a05e03..3d0c59a353 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@hmcts/ccd-case-ui-toolkit", - "version": "7.1.22", + "version": "7.1.23", "engines": { "node": ">=18.19.0" }, diff --git a/projects/ccd-case-ui-toolkit/package.json b/projects/ccd-case-ui-toolkit/package.json index 350cda0a43..22b57ace08 100644 --- a/projects/ccd-case-ui-toolkit/package.json +++ b/projects/ccd-case-ui-toolkit/package.json @@ -1,6 +1,6 @@ { "name": "@hmcts/ccd-case-ui-toolkit", - "version": "7.1.22", + "version": "7.1.23", "engines": { "node": ">=18.19.0" },