diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index c4ccf2b11b..2dc4285e58 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,5 +1,10 @@ ## RELEASE NOTES +### Version 7.1.32-rc1 +**EXUI-2551** Case File view too small not loading with correct size +**EXUI-2104** Event History Summary - ExUI Changes +**EXUI-2668** when Update Referral event completed (incorrect) + ### Version 7.1.32 **EXUI-2729** Query Management - Callback error handling. @@ -34,7 +39,7 @@ Taken by PR **EXUI-2389** PED and Media Viewer ### Version 7.0.75-exui-2315 -**EXUI-2315** etrieve current user language selection +**EXUI-2315** Retrieve current user language selection ### Version 7.0.75-exui-2515 diff --git a/package.json b/package.json index 8009b29d3b..fe6f53d336 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@hmcts/ccd-case-ui-toolkit", - "version": "7.1.32", + "version": "7.1.32-srt-fix-task-completion-2", "engines": { "node": ">=18.19.0" }, diff --git a/projects/ccd-case-ui-toolkit/package.json b/projects/ccd-case-ui-toolkit/package.json index b79f1b215c..6778f66ab1 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.32", + "version": "7.1.32-srt-fix-task-completion-2", "engines": { "node": ">=18.19.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 index 32525da16f..8571636f43 100644 --- 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 @@ -1,4 +1,5 @@ -import { CaseEditUtils, convertNonASCIICharacter } from "./case-edit.utils"; +import { SessionStorageService } from "../../../services"; +import { CaseEditUtils, convertNonASCIICharacter, removeTaskFromClientContext } from "./case-edit.utils"; describe('CaseEditUtils', () => { const caseUtils: CaseEditUtils = new CaseEditUtils(); @@ -61,4 +62,34 @@ describe('CaseEditUtils', () => { }); }); + describe('removeTaskFromClientContext', () => { + const fullMockClientContext = { client_context: { user_task: 'task', user_language: 'language' } }; + const partialMockClientContext = { client_context: { user_language: 'language' } }; + + let mockSessionStorageService; + + beforeEach(() => { + mockSessionStorageService = jasmine.createSpyObj('SessionStorageService', ['getItem', 'removeItem', 'setItem']); + }) + + it('should correctly remove task from client context', () => { + mockSessionStorageService.getItem.and.returnValue(JSON.stringify(fullMockClientContext)); + removeTaskFromClientContext(mockSessionStorageService); + expect(mockSessionStorageService.setItem).toHaveBeenCalledWith('clientContext', JSON.stringify(partialMockClientContext)); + }); + + it('should do nothing if there is no session storage service', () => { + removeTaskFromClientContext(null); + mockSessionStorageService.getItem.and.returnValue(null); + removeTaskFromClientContext(mockSessionStorageService); + expect(mockSessionStorageService.setItem).not.toHaveBeenCalled(); + }); + + it('should do nothing if there is no user_task', () => { + mockSessionStorageService.getItem.and.returnValue(JSON.stringify(partialMockClientContext)); + removeTaskFromClientContext(mockSessionStorageService); + expect(mockSessionStorageService.setItem).not.toHaveBeenCalled(); + }); + }); + }); 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 index c5c42061d6..88c7e7239d 100644 --- 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 @@ -1,3 +1,5 @@ +import { SessionStorageService } from "../../../services"; +import { CaseEditComponent } from "../case-edit/case-edit.component"; export function convertNonASCIICharacter(character: string): string { if (character === '£') { @@ -36,3 +38,15 @@ export class CaseEditUtils { return rawString; } } + +export function removeTaskFromClientContext(sessionStorageService: SessionStorageService): void { + if (!sessionStorageService) { + return; + } + const clientContextString = sessionStorageService.getItem(CaseEditComponent.CLIENT_CONTEXT); + const clientContext = clientContextString ? JSON.parse(clientContextString) : null; + if (clientContext?.client_context?.user_task) { + delete clientContext.client_context.user_task; + sessionStorageService.setItem(CaseEditComponent.CLIENT_CONTEXT, JSON.stringify(clientContext)); + } +} diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-edit/case-edit.component.spec.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-edit/case-edit.component.spec.ts index 43b38d3b6b..ae1fe03185 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-edit/case-edit.component.spec.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-edit/case-edit.component.spec.ts @@ -11,7 +11,7 @@ import { CaseEventTrigger } from '../../../domain/case-view/case-event-trigger.m import { CaseField } from '../../../domain/definition/case-field.model'; import { createCaseEventTrigger } from '../../../fixture/shared.test.fixture'; import { FieldsFilterPipe } from '../../../pipes/complex/fields-filter.pipe'; -import { AlertService, FieldsPurger, FieldsUtils, LoadingService, SessionStorageService, WindowService } from '../../../services'; +import { AlertService, FieldsPurger, FieldsUtils, LoadingService, ReadCookieService, SessionStorageService, WindowService } from '../../../services'; import { FormErrorService, FormValueService } from '../../../services/form'; import { PaletteUtilsModule } from '../../palette'; import { Confirmation, Wizard, WizardPage, WizardPageField } from '../domain'; @@ -227,6 +227,7 @@ describe('CaseEditComponent', () => { let mockSessionStorageService: jasmine.SpyObj; let mockWorkAllocationService: jasmine.SpyObj; let mockAlertService: jasmine.SpyObj; + let mockCookieService: jasmine.SpyObj; let mockabstractConfig: jasmine.SpyObj; const validPageListCaseFieldsService = new ValidPageListCaseFieldsService(fieldsUtils); @@ -289,6 +290,7 @@ describe('CaseEditComponent', () => { mockSessionStorageService = createSpyObj('SessionStorageService', ['getItem', 'removeItem', 'setItem']); mockWorkAllocationService = createSpyObj('WorkAllocationService', ['assignAndCompleteTask', 'completeTask']); mockAlertService = createSpyObj('AlertService', ['error', 'setPreserveAlerts']); + mockCookieService = createSpyObj('ReadCookieService', ['getCookie']); mockabstractConfig = createSpyObj('AbstractAppConfig', ['logMessage']); spyOn(validPageListCaseFieldsService, 'deleteNonValidatedFields'); spyOn(validPageListCaseFieldsService, 'validPageListCaseFields'); @@ -342,6 +344,7 @@ describe('CaseEditComponent', () => { { provide: WorkAllocationService, useValue: mockWorkAllocationService}, { provide: SessionStorageService, useValue: mockSessionStorageService}, { provide: AlertService, useValue: mockAlertService }, + { provide: ReadCookieService, useValue: mockCookieService }, WindowService, { provide: LoadingService, loadingServiceMock }, { provide: ValidPageListCaseFieldsService, useValue: validPageListCaseFieldsService}, @@ -1242,8 +1245,8 @@ describe('CaseEditComponent', () => { expect(validPageListCaseFieldsService.validPageListCaseFields).toHaveBeenCalled(); expect(formValueService.removeUnnecessaryFields).toHaveBeenCalled(); // check that tasks removed from session storage once event has been completed - expect(mockSessionStorageService.removeItem).toHaveBeenCalledWith('clientContext'); - expect(mockSessionStorageService.removeItem).toHaveBeenCalledWith('taskEventCompletionInfo'); + expect(mockSessionStorageService.removeItem).toHaveBeenCalledWith(CaseEditComponent.CLIENT_CONTEXT); + expect(mockSessionStorageService.removeItem).toHaveBeenCalledWith(CaseEditComponent.TASK_EVENT_COMPLETION_INFO); }); it('should submit the case for a Case Flags submission', () => { @@ -1526,7 +1529,7 @@ describe('CaseEditComponent', () => { }); it('should return true when there is a task present that matches the current case when there is no event in session storage', () => { - const mockTask = {id: '123', case_id: '123456789'}; + const mockTask = {id: '123', case_id: '123456789', description: 'test test test [testEvent]'}; expect(component.taskExistsForThisEvent(mockTask as Task, null, mockEventDetails)).toBe(true); }); diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-edit/case-edit.component.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-edit/case-edit.component.ts index 30565c51d4..5589bfaf72 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-edit/case-edit.component.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-edit/case-edit.component.ts @@ -19,12 +19,14 @@ import { EventDetails, Task, TaskEventCompletionInfo } from '../../../domain/wor import { AlertService, FieldsPurger, FieldsUtils, FormErrorService, FormValueService, LoadingService, + ReadCookieService, SessionStorageService, WindowService } from '../../../services'; import { Confirmation, Wizard, WizardPage } from '../domain'; import { EventCompletionParams } from '../domain/event-completion-params.model'; import { CaseNotifier, WizardFactoryService, WorkAllocationService } from '../services'; import { ValidPageListCaseFieldsService } from '../services/valid-page-list-caseFields.service'; +import { removeTaskFromClientContext } from '../case-edit-utils/case-edit.utils'; @Component({ selector: 'ccd-case-edit', @@ -35,6 +37,8 @@ import { ValidPageListCaseFieldsService } from '../services/valid-page-list-case export class CaseEditComponent implements OnInit, OnDestroy { public static readonly ORIGIN_QUERY_PARAM = 'origin'; public static readonly ALERT_MESSAGE = 'Page is being refreshed so you will be redirected to the first page of this event.'; + public static readonly CLIENT_CONTEXT = 'clientContext'; + public static readonly TASK_EVENT_COMPLETION_INFO = 'taskEventCompletionInfo'; @Input() public eventTrigger: CaseEventTrigger; @@ -106,7 +110,8 @@ export class CaseEditComponent implements OnInit, OnDestroy { private readonly validPageListCaseFieldsService: ValidPageListCaseFieldsService, private readonly workAllocationService: WorkAllocationService, private readonly alertService: AlertService, - private readonly abstractConfig: AbstractAppConfig + private readonly abstractConfig: AbstractAppConfig, + private readonly cookieService: ReadCookieService ) {} public ngOnInit(): void { @@ -238,12 +243,12 @@ export class CaseEditComponent implements OnInit, OnDestroy { this.isSubmitting = true; // We have to run the event completion checks if task in session storage // and if the task is in session storage, then is it associated to the case - const clientContextStr = this.sessionStorageService.getItem('clientContext'); + const clientContextStr = this.sessionStorageService.getItem(CaseEditComponent.CLIENT_CONTEXT); const userTask = FieldsUtils.getUserTaskFromClientContext(clientContextStr); const taskInSessionStorage = userTask ? userTask.task_data : null; let taskEventCompletionInfo: TaskEventCompletionInfo; let userInfo: UserInfo; - const taskEventCompletionStr = this.sessionStorageService.getItem('taskEventCompletionInfo'); + const taskEventCompletionStr = this.sessionStorageService.getItem(CaseEditComponent.TASK_EVENT_COMPLETION_INFO); const userInfoStr = this.sessionStorageService.getItem('userDetails'); const assignNeeded = this.sessionStorageService.getItem('assignNeeded'); if (taskEventCompletionStr) { @@ -272,7 +277,7 @@ export class CaseEditComponent implements OnInit, OnDestroy { userId, taskId: taskInSessionStorage.id, createdTimestamp: Date.now()}; - this.sessionStorageService.setItem('taskEventCompletionInfo', JSON.stringify(taskEventCompletionInfo)); + this.sessionStorageService.setItem(CaseEditComponent.TASK_EVENT_COMPLETION_INFO, JSON.stringify(taskEventCompletionInfo)); this.isEventCompletionChecksRequired = true; } else { // Task not in session storage, proceed to submit @@ -460,8 +465,9 @@ export class CaseEditComponent implements OnInit, OnDestroy { }),finalize(() => { this.loadingService.unregister(loadingSpinnerToken); // on event completion ensure the previous event clientContext/taskEventCompletionInfo removed - this.sessionStorageService.removeItem('clientContext'); - this.sessionStorageService.removeItem('taskEventCompletionInfo') + // Note - Not removeTaskFromClientContext because could interfere with other logic + this.sessionStorageService.removeItem(CaseEditComponent.CLIENT_CONTEXT); + this.sessionStorageService.removeItem(CaseEditComponent.TASK_EVENT_COMPLETION_INFO) this.isSubmitting = false; })) .subscribe( @@ -492,7 +498,7 @@ export class CaseEditComponent implements OnInit, OnDestroy { } private postCompleteTaskIfRequired(): Observable { - const clientContextStr = this.sessionStorageService.getItem('clientContext'); + const clientContextStr = this.sessionStorageService.getItem(CaseEditComponent.CLIENT_CONTEXT); const userTask = FieldsUtils.getUserTaskFromClientContext(clientContextStr); const [task, taskToBeCompleted] = userTask ? [userTask.task_data, userTask.complete_task] : [null, false]; const assignNeeded = this.sessionStorageService.getItem('assignNeeded') === 'true'; @@ -537,7 +543,14 @@ export class CaseEditComponent implements OnInit, OnDestroy { } if (!taskEventCompletionInfo) { // if no task event present then there is no task to complete from previous event present - return true; + // EXUI-2668 - Add additional logic to confirm the task is relevant to the event + if (this.taskIsForEvent(taskInSessionStorage, eventDetails)) { + return true; + } else { + // client context still needed for language + removeTaskFromClientContext(this.sessionStorageService); + return false; + } } else { if (taskEventCompletionInfo.taskId !== taskInSessionStorage.id) { return true; @@ -546,8 +559,8 @@ export class CaseEditComponent implements OnInit, OnDestroy { || this.eventMoreThanDayAgo(taskEventCompletionInfo.createdTimestamp) ) { // if the session storage not related to event, ignore it and remove - this.sessionStorageService.removeItem('clientContext'); - this.sessionStorageService.removeItem('taskEventCompletionInfo'); + removeTaskFromClientContext(this.sessionStorageService); + this.sessionStorageService.removeItem(CaseEditComponent.TASK_EVENT_COMPLETION_INFO); return false; } if (eventDetails.assignNeeded === 'false' && eventDetails.userId !== taskInSessionStorage.assignee) { @@ -593,4 +606,12 @@ export class CaseEditComponent implements OnInit, OnDestroy { } return false; } + + private taskIsForEvent(task: Task, eventDetails: EventDetails): boolean { + // EXUI-2668 - Ensure description for task includes event ID + // Note - This is a failsafe for an edge case that may never occur again + // Description may not include eventId in some cases which may mean task not completed (however this will be easy to check) + this.abstractConfig.logMessage(`checking taskIsForEvent: task ID ${task.id}, task description ${task.description}, event name ${eventDetails.eventId}`); + return task.case_id === eventDetails.caseId && task.description?.includes(eventDetails.eventId); + } } diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-event-completion/components/case-event-completion-task-cancelled/case-event-completion-task-cancelled.component.spec.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-event-completion/components/case-event-completion-task-cancelled/case-event-completion-task-cancelled.component.spec.ts index c2c6a76cae..4d22301080 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-event-completion/components/case-event-completion-task-cancelled/case-event-completion-task-cancelled.component.spec.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-event-completion/components/case-event-completion-task-cancelled/case-event-completion-task-cancelled.component.spec.ts @@ -2,6 +2,7 @@ import { Component, Input, ViewChild } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { MockRpxTranslatePipe } from '../../../../../test/mock-rpx-translate.pipe'; +import { CaseEditComponent } from '../../../case-edit'; import { EventCompletionStateMachineContext } from '../../../domain'; import { CaseEventCompletionTaskCancelledComponent } from './case-event-completion-task-cancelled.component'; @@ -46,7 +47,7 @@ describe('TaskCancelledComponent', () => { it('should emit event can be completed true when clicked on continue button', () => { spyOn(component.notifyEventCompletionCancelled, 'emit'); component.onContinue(); - expect(component.context.sessionStorageService.removeItem).toHaveBeenCalledWith('clientContext') + expect(component.context.sessionStorageService.removeItem).toHaveBeenCalledWith(CaseEditComponent.CLIENT_CONTEXT); expect(component.notifyEventCompletionCancelled.emit).toHaveBeenCalledWith(true); }); diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-event-completion/components/case-event-completion-task-cancelled/case-event-completion-task-cancelled.component.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-event-completion/components/case-event-completion-task-cancelled/case-event-completion-task-cancelled.component.ts index c2f5840dbb..de31081a8f 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-event-completion/components/case-event-completion-task-cancelled/case-event-completion-task-cancelled.component.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-event-completion/components/case-event-completion-task-cancelled/case-event-completion-task-cancelled.component.ts @@ -1,6 +1,6 @@ import { Component, EventEmitter, Inject, Input, OnInit, Output } from '@angular/core'; -import { COMPONENT_PORTAL_INJECTION_TOKEN, CaseEventCompletionComponent } from '../../case-event-completion.component'; import { EventCompletionStateMachineContext } from '../../../domain'; +import { CaseEditComponent } from '../../../case-edit'; @Component({ selector: 'app-case-event-completion-task-cancelled', @@ -20,7 +20,7 @@ export class CaseEventCompletionTaskCancelledComponent implements OnInit { public onContinue(): void { // Removes task to complete so event completes without task - this.context.sessionStorageService.removeItem('clientContext'); + this.context.sessionStorageService.removeItem(CaseEditComponent.CLIENT_CONTEXT); // may be able to remove this call below since it is now unneccesary this.notifyEventCompletionCancelled.emit(true); } diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-event-completion/components/case-event-completion-task-reassigned/case-event-completion-task-reassigned.component.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-event-completion/components/case-event-completion-task-reassigned/case-event-completion-task-reassigned.component.ts index 1b55aafa0e..ad267025f8 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-event-completion/components/case-event-completion-task-reassigned/case-event-completion-task-reassigned.component.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/case-event-completion/components/case-event-completion-task-reassigned/case-event-completion-task-reassigned.component.ts @@ -7,6 +7,7 @@ import { import { EventCompletionStateMachineContext } from '../../../domain'; import { CaseworkerService } from '../../../services/case-worker.service'; import { JudicialworkerService } from '../../../services/judicial-worker.service'; +import { CaseEditComponent } from '../../../case-edit'; @Component({ selector: 'app-case-event-completion-task-reassigned', @@ -77,7 +78,7 @@ export class CaseEventCompletionTaskReassignedComponent implements OnInit, OnDes public onContinue(): void { // Get task details - const clientContextStr = this.sessionStorageService.getItem('clientContext'); + const clientContextStr = this.sessionStorageService.getItem(CaseEditComponent.CLIENT_CONTEXT); const userTask = FieldsUtils.getUserTaskFromClientContext(clientContextStr); const task = userTask ? userTask.task_data : null; // not complete_task not utilised here as related to event completion 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 299d0d8743..a29f5bc504 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 @@ -22,6 +22,7 @@ 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'; +import { CaseEditComponent } from '../case-edit'; @Injectable() export class CasesService { @@ -395,7 +396,7 @@ export class CasesService { } private addClientContextHeader(headers: HttpHeaders): HttpHeaders { - const clientContextDetails = this.sessionStorageService.getItem('clientContext'); + const clientContextDetails = this.sessionStorageService.getItem(CaseEditComponent.CLIENT_CONTEXT); if (clientContextDetails) { const caseEditUtils = new CaseEditUtils(); // below changes non-ASCII characters @@ -414,7 +415,7 @@ export class CasesService { const clientContextString = window.atob(headers.get('Client-Context')); // below reverts non-ASCII characters const editedClientContextString = caseEditUtils.convertHTMLEntities(clientContextString); - this.sessionStorageService.setItem('clientContext', editedClientContextString); + this.sessionStorageService.setItem(CaseEditComponent.CLIENT_CONTEXT, editedClientContextString); } } diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/services/event-completion-state-machine.service.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/services/event-completion-state-machine.service.ts index 99d1114dbb..2a38ddfe7c 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/services/event-completion-state-machine.service.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-editor/services/event-completion-state-machine.service.ts @@ -6,6 +6,7 @@ import { FieldsUtils } from '../../../services'; import { EventCompletionStateMachineContext } from '../domain/event-completion-state-machine-context.model'; import { EventCompletionStates } from '../domain/event-completion-states.enum.model'; import { EventCompletionTaskStates } from '../domain/event-completion-task-states.model'; +import { CaseEditComponent } from '../case-edit'; const EVENT_COMPLETION_STATE_MACHINE = 'EVENT COMPLETION STATE MACHINE'; @@ -139,7 +140,7 @@ export class EventCompletionStateMachineService { public entryActionForStateCompleteEventAndTask(state: State, context: EventCompletionStateMachineContext): void { // Trigger final state to complete processing of state machine state.trigger(EventCompletionStates.Final); - const clientContextStr = context.sessionStorageService.getItem('clientContext'); + const clientContextStr = context.sessionStorageService.getItem(CaseEditComponent.CLIENT_CONTEXT); const userTask = FieldsUtils.getUserTaskFromClientContext(clientContextStr); if (userTask?.task_data) { context.sessionStorageService.setItem('assignNeeded', 'false'); @@ -163,7 +164,7 @@ export class EventCompletionStateMachineService { public entryActionForStateTaskUnassigned(state: State, context: EventCompletionStateMachineContext): void { // Trigger final state to complete processing of state machine state.trigger(EventCompletionStates.Final); - const clientContextStr = context.sessionStorageService.getItem('clientContext'); + const clientContextStr = context.sessionStorageService.getItem(CaseEditComponent.CLIENT_CONTEXT); const userTask = FieldsUtils.getUserTaskFromClientContext(clientContextStr); if (userTask?.task_data) { context.sessionStorageService.setItem('assignNeeded', 'true'); @@ -233,7 +234,7 @@ export class EventCompletionStateMachineService { } public taskPresentInSessionStorage(context: EventCompletionStateMachineContext): boolean { - const clientContextStr = context.sessionStorageService.getItem('clientContext'); + const clientContextStr = context.sessionStorageService.getItem(CaseEditComponent.CLIENT_CONTEXT); const userTask = FieldsUtils.getUserTaskFromClientContext(clientContextStr); return !!userTask.task_data; } diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-history/case-history.component.html b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-history/case-history.component.html index 8e9be1973f..34552ae5fe 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-history/case-history.component.html +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-history/case-history.component.html @@ -49,15 +49,13 @@

{{tab.label}}

{{field.label}}
- - - + - + - + diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-history/case-history.component.spec.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-history/case-history.component.spec.ts index 731d0b9462..65aef250e1 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-history/case-history.component.spec.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/case-history/case-history.component.spec.ts @@ -205,7 +205,7 @@ describe('CaseHistoryComponent', () => { it('should render each field value using FieldReadComponent', () => { const readFields = de .query($NAME_TAB_CONTENT) - .queryAll(By.css('tr td>ccd-field-read')); + .queryAll(By.css('tr th>ccd-field-read')); FIELDS.forEach(field => { const readField = readFields.find(f => { 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 8636750f2a..1062406dd8 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 @@ -4,7 +4,7 @@ import { of } from 'rxjs'; import { TaskPayload } from '../../../domain/work-allocation/TaskPayload'; import { UserInfo } from '../../../domain/user/user-info.model'; import { ReadCookieService, SessionStorageService } from '../../../services'; -import { WorkAllocationService } from '../../case-editor'; +import { CaseEditComponent, WorkAllocationService } from '../../case-editor'; import { EventStartGuard } from './event-start.guard'; import { AbstractAppConfig } from '../../../../app.config'; @@ -76,7 +76,7 @@ describe('EventStartGuard', () => { result$.subscribe(result => { expect(result).toEqual(false); // check client context is set correctly - expect(sessionStorageService.setItem).toHaveBeenCalledWith('clientContext', JSON.stringify(mockClientContext)); + expect(sessionStorageService.setItem).toHaveBeenCalledWith(CaseEditComponent.CLIENT_CONTEXT, JSON.stringify(mockClientContext)); }); }); @@ -90,7 +90,7 @@ describe('EventStartGuard', () => { result$.subscribe(result => { expect(result).toEqual(false); // check client context is set correctly - expect(sessionStorageService.setItem).toHaveBeenCalledWith('clientContext', JSON.stringify(mockClientContext)); + expect(sessionStorageService.setItem).toHaveBeenCalledWith(CaseEditComponent.CLIENT_CONTEXT, JSON.stringify(mockClientContext)); }); }); @@ -183,7 +183,7 @@ describe('EventStartGuard', () => { 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)); + expect(sessionStorageService.setItem).toHaveBeenCalledWith(CaseEditComponent.CLIENT_CONTEXT, JSON.stringify(clientContext)); }); it('should return false with error navigation if there are more than 1 tasks assigned to the user', () => { @@ -214,7 +214,7 @@ describe('EventStartGuard', () => { 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)); + expect(sessionStorageService.setItem).toHaveBeenCalledWith(CaseEditComponent.CLIENT_CONTEXT, 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 2472f22c03..5a10043855 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 @@ -7,11 +7,11 @@ import { AbstractAppConfig } from '../../../../app.config'; import { TaskEventCompletionInfo } from '../../../domain/work-allocation/Task'; import { TaskPayload } from '../../../domain/work-allocation/TaskPayload'; import { ReadCookieService, SessionStorageService } from '../../../services'; -import { WorkAllocationService } from '../../case-editor'; +import { CaseEditComponent, WorkAllocationService } from '../../case-editor'; +import { removeTaskFromClientContext } from '../../case-editor/case-edit-utils/case-edit.utils'; @Injectable() export class EventStartGuard implements CanActivate { - public static readonly CLIENT_CONTEXT = 'clientContext'; constructor(private readonly workAllocationService: WorkAllocationService, private readonly router: Router, @@ -33,7 +33,7 @@ export class EventStartGuard implements CanActivate { 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); + const preClientContext = this.sessionStorageService.getItem(CaseEditComponent.CLIENT_CONTEXT); if (!preClientContext) { // creates client context for language if not already existing const storeClientContext = { @@ -43,7 +43,7 @@ export class EventStartGuard implements CanActivate { } } }; - this.sessionStorageService.setItem(EventStartGuard.CLIENT_CONTEXT, JSON.stringify(storeClientContext)); + this.sessionStorageService.setItem(CaseEditComponent.CLIENT_CONTEXT, JSON.stringify(storeClientContext)); } else { const clientContextObj = JSON.parse(preClientContext); if (!clientContextObj?.client_context?.user_language) { @@ -56,7 +56,7 @@ export class EventStartGuard implements CanActivate { } } } - this.sessionStorageService.setItem(EventStartGuard.CLIENT_CONTEXT, JSON.stringify(clientContextAddLanguage)); + this.sessionStorageService.setItem(CaseEditComponent.CLIENT_CONTEXT, JSON.stringify(clientContextAddLanguage)); } } if (caseInfoStr) { @@ -117,15 +117,11 @@ export class EventStartGuard implements CanActivate { } } }; - this.sessionStorageService.setItem(EventStartGuard.CLIENT_CONTEXT, JSON.stringify(storeClientContext)); + this.sessionStorageService.setItem(CaseEditComponent.CLIENT_CONTEXT, JSON.stringify(storeClientContext)); return true; } } - private removeTaskFromSessionStorage(): void { - this.sessionStorageService.removeItem(EventStartGuard.CLIENT_CONTEXT); - } - private checkForTasks(payload: TaskPayload, caseId: string, eventId: string, taskId: string, userId: string): Observable { if (taskId && payload?.tasks?.length > 0) { const task = payload.tasks.find((t) => t.id == taskId); @@ -150,10 +146,10 @@ export class EventStartGuard implements CanActivate { } } }; - this.sessionStorageService.setItem('taskEventCompletionInfo', JSON.stringify(taskEventCompletionInfo)); - this.sessionStorageService.setItem(EventStartGuard.CLIENT_CONTEXT, JSON.stringify(storeClientContext)); + this.sessionStorageService.setItem(CaseEditComponent.TASK_EVENT_COMPLETION_INFO, JSON.stringify(taskEventCompletionInfo)); + this.sessionStorageService.setItem(CaseEditComponent.CLIENT_CONTEXT, JSON.stringify(storeClientContext)); } else { - this.removeTaskFromSessionStorage(); + removeTaskFromClientContext(this.sessionStorageService); } } if (payload.task_required_for_event) { 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 5b95b4cf08..10e7474326 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 @@ -4,6 +4,7 @@ import { State, StateMachine } from '@edium/fsm'; import { EventStartStateMachineContext, EventStartStates } from '../models'; import { TaskEventCompletionInfo } from '../../../domain/work-allocation/Task'; import { UserInfo } from '../../../domain/user/user-info.model'; +import { CaseEditComponent } from '../../case-editor'; const EVENT_STATE_MACHINE = 'EVENT STATE MACHINE'; @@ -199,7 +200,6 @@ export class EventStartStateMachineService { } } }; - context.sessionStorageService.setItem('clientContext', JSON.stringify(clientContext)); let userInfo: UserInfo; const userInfoStr = context.sessionStorageService.getItem('userDetails'); if (userInfoStr) { @@ -212,7 +212,9 @@ export class EventStartStateMachineService { userId: userInfo.id ? userInfo.id : userInfo.uid, taskId: task.id, createdTimestamp: Date.now()}; - context.sessionStorageService.setItem('taskEventCompletionInfo', JSON.stringify(taskEventCompletionInfo)); + context.sessionStorageService.setItem(CaseEditComponent.TASK_EVENT_COMPLETION_INFO, JSON.stringify(taskEventCompletionInfo)); + // EXUI-2668 - Only add client context when taskEventCompletionInfo is set - stops auto completing incorrect tasks + context.sessionStorageService.setItem(CaseEditComponent.CLIENT_CONTEXT, JSON.stringify(clientContext)); // Allow user to perform the event context.router.navigate([`/cases/case-details/${context.caseId}/trigger/${context.eventId}`], { relativeTo: context.route }); diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/history/event-log/event-log-table.component.html b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/history/event-log/event-log-table.component.html index a73525fd97..7cf23336ed 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/history/event-log/event-log-table.component.html +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/history/event-log/event-log-table.component.html @@ -13,17 +13,19 @@
{{event.event_name | rpxTranslate}} + *ngIf="event.state_id !== 'Draft' && !isUserExternal" class="text-16 event-link" attr.aria-label="{{getAriaLabelforLink(event, isUserExternal)}}">{{event.event_name | rpxTranslate}} + {{event.event_name | rpxTranslate}}
{{event.event_name | rpxTranslate}} + *ngIf="event.state_id !== 'Draft' && !isUserExternal" class="text-16 event-link" attr.aria-label="{{getAriaLabelforLink(event, isUserExternal)}}">{{event.event_name | rpxTranslate}} + {{event.event_name | rpxTranslate}}
{{event.event_name}}
- + document image {{getSignificantItemDesc(event)}} diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/history/event-log/event-log-table.component.spec.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/history/event-log/event-log-table.component.spec.ts index f36a4ea626..e401813345 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/history/event-log/event-log-table.component.spec.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/history/event-log/event-log-table.component.spec.ts @@ -9,8 +9,10 @@ import { MockRpxTranslatePipe } from '../../../../test/mock-rpx-translate.pipe'; import { DatePipe } from '../../utils'; import { EventLogTableComponent } from './event-log-table.component'; import createSpyObj = jasmine.createSpyObj; +import { SessionStorageService } from '../../../../services'; describe('EventLogTableComponent', () => { + const mockSessionStorageService = jasmine.createSpyObj('SessionStorageService', ['getItem']); const EVENTS: CaseViewEvent[] = [ { @@ -57,6 +59,13 @@ describe('EventLogTableComponent', () => { const $TABLE_ROW_LINKS_TIMELINE = By.css('table>tbody>tr>td>div#case-timeline>a'); const $TABLE_ROW_LINKS_STANDALONE = By.css('table>tbody>tr>td>div:not(.tooltip)>a'); const $TABLE_COLUMNS = By.css('table>tbody>tr>td'); + const $TABLE_ROW_LINKS_TIMELINE_EXTERNAL = By.css('table>tbody>tr>td>div>span.event-link'); + + const USER = { + roles: [ + 'caseworker' + ] + }; const COL_EVENT = 0; const COL_DATE = 1; @@ -76,12 +85,15 @@ describe('EventLogTableComponent', () => { DatePipe, MockRpxTranslatePipe ], - providers: [FormatTranslatorService] + providers: [FormatTranslatorService, + { provide: SessionStorageService, useValue: mockSessionStorageService } + ] }) .compileComponents(); fixture = TestBed.createComponent(EventLogTableComponent); component = fixture.componentInstance; + mockSessionStorageService.getItem.and.returnValue(JSON.stringify(USER)); component.events = EVENTS; component.selected = SELECTED_EVENT; @@ -199,10 +211,8 @@ describe('EventLogTableComponent', () => { }); describe('Case timeline use', () => { - let caseHistoryHandler; beforeEach(waitForAsync(() => { - caseHistoryHandler = createSpyObj('caseHistoryHandler', ['apply']); TestBed @@ -213,12 +223,16 @@ describe('EventLogTableComponent', () => { DatePipe, MockRpxTranslatePipe ], - providers: [FormatTranslatorService] + providers: [FormatTranslatorService, + { provide: SessionStorageService, useValue: mockSessionStorageService } + + ] }) .compileComponents(); fixture = TestBed.createComponent(EventLogTableComponent); component = fixture.componentInstance; + mockSessionStorageService.getItem.and.returnValue(JSON.stringify(USER)); component.events = EVENTS; component.selected = SELECTED_EVENT; @@ -238,4 +252,48 @@ describe('EventLogTableComponent', () => { expect(caseHistoryHandler.apply).toHaveBeenCalledWith(4); }); }); + + describe('External user', () => { + beforeEach(waitForAsync(() => { + TestBed + .configureTestingModule({ + imports: [RouterTestingModule], + declarations: [ + EventLogTableComponent, + DatePipe, + MockRpxTranslatePipe + ], + providers: [FormatTranslatorService, + { provide: SessionStorageService, useValue: mockSessionStorageService } + ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(EventLogTableComponent); + component = fixture.componentInstance; + const EXTERNAL_USER = { + roles: [ + 'pui-case-manager' + ] + }; + mockSessionStorageService.getItem.and.returnValue(JSON.stringify(EXTERNAL_USER)); + + component.events = EVENTS; + component.selected = SELECTED_EVENT; + component.isPartOfCaseTimeline = false; + + de = fixture.debugElement; + fixture.detectChanges(); + })); + + it('should not render hyperlink for each row and link to event id', () => { + const links = de.queryAll($TABLE_ROW_LINKS_TIMELINE_EXTERNAL); + expect(component.isUserExternal).toBeTruthy(); + expect(links.length).toBe(2); + expect(links[0].nativeElement.getAttribute('href')).toBeNull(); + expect(links[1].nativeElement.getAttribute('href')).toBeNull(); + expect(links[0].nativeElement.textContent).toContain('Update a case'); + expect(links[1].nativeElement.textContent).toContain('Update a case'); + }); + }); }); diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/history/event-log/event-log-table.component.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/history/event-log/event-log-table.component.ts index 08420b3178..0b4dddea2e 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/history/event-log/event-log-table.component.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/components/palette/history/event-log/event-log-table.component.ts @@ -1,6 +1,7 @@ import { formatDate } from '@angular/common'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { CaseViewEvent } from '../../../../domain'; +import { SessionStorageService } from '../../../../services'; @Component({ selector: 'ccd-event-log-table', @@ -8,7 +9,6 @@ import { CaseViewEvent } from '../../../../domain'; styleUrls: ['./event-log-table.scss'] }) export class EventLogTableComponent implements OnInit { - @Input() public events: CaseViewEvent[]; @@ -23,8 +23,15 @@ export class EventLogTableComponent implements OnInit { public isPartOfCaseTimeline = false; + public isUserExternal: boolean; + + constructor( + private readonly sessionStorage: SessionStorageService + ){} + public ngOnInit(): void { this.isPartOfCaseTimeline = this.onCaseHistory.observers.length > 0; + this.isUserExternal = JSON.parse(this.sessionStorage.getItem('userDetails')).roles.includes('pui-case-manager'); } public select(event: CaseViewEvent): void { @@ -68,7 +75,10 @@ export class EventLogTableComponent implements OnInit { return `you are on event ${event.event_name} row, press tab key to navigate to columns`; } - public getAriaLabelforLink(event: CaseViewEvent): string { + public getAriaLabelforLink(event: CaseViewEvent, isExternalUser: boolean): string { + if (isExternalUser) { + return `${event.event_name}`; + } return `press enter key to open event ${event.event_name} link in separate window`; } } diff --git a/projects/ccd-case-ui-toolkit/src/lib/shared/domain/work-allocation/Task.ts b/projects/ccd-case-ui-toolkit/src/lib/shared/domain/work-allocation/Task.ts index be72e165aa..02b0742b4e 100644 --- a/projects/ccd-case-ui-toolkit/src/lib/shared/domain/work-allocation/Task.ts +++ b/projects/ccd-case-ui-toolkit/src/lib/shared/domain/work-allocation/Task.ts @@ -8,6 +8,7 @@ export interface Task { case_type_id?: string; created_date: string; due_date?: string; + description?: string; execution_type?: string; id: string; jurisdiction: string;