diff --git a/src/app/forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history-edit.component.html b/src/app/forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history-edit.component.html
index aa75a8a9dd..b84be43f86 100644
--- a/src/app/forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history-edit.component.html
+++ b/src/app/forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history-edit.component.html
@@ -8,23 +8,34 @@
0; else empty">
-
-
+
+
+
-
+
- {{ examinerNote.createdAtDate | date : 'dd/MM/yyyy' | defaultNullOrEmpty }}
+
|
{{ examinerNote.lastUpdatedBy }} |
- {{ examinerNote.note }}
+ {{ examinerNote.createdAtDate | date: 'dd/MM/yyyy HH:mm' | defaultNullOrEmpty }}
+ |
+
+ Edit
|
+
diff --git a/src/app/forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit.scss b/src/app/forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit.scss
index dc97c176aa..2df9a0b23d 100644
--- a/src/app/forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit.scss
+++ b/src/app/forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit.scss
@@ -15,4 +15,10 @@
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 5;
-}
\ No newline at end of file
+}
+
+.button {
+ cursor: pointer;
+ text-decoration: underline;
+ color: blue;
+}
diff --git a/src/app/forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit.spec.ts b/src/app/forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit.spec.ts
index 8f3255936e..76ff27c32c 100644
--- a/src/app/forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit.spec.ts
+++ b/src/app/forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit.spec.ts
@@ -8,6 +8,11 @@ import { DynamicFormsModule } from '@forms/dynamic-forms.module';
import { mockVehicleTechnicalRecord } from '@mocks/mock-vehicle-technical-record.mock';
import { VehicleTypes } from '@models/vehicle-tech-record.model';
import { TechnicalRecordService } from '@services/technical-record/technical-record.service';
+import { provideMockStore } from '@ngrx/store/testing';
+import { initialAppState } from '@store/index';
+import { ActivatedRoute, Router } from '@angular/router';
+import { of } from 'rxjs';
+import { RouterTestingModule } from '@angular/router/testing';
const mockTechRecordService = {
techRecord$: jest.fn(),
@@ -15,16 +20,23 @@ const mockTechRecordService = {
describe('AdrExaminerNotesHistoryEditComponent', () => {
let component: AdrExaminerNotesHistoryEditComponent;
let fixture: ComponentFixture ;
+ let router: Router;
+
+ const MOCK_HGV = mockVehicleTechnicalRecord(VehicleTypes.HGV) as TechRecordType<'hgv'>;
+
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [AdrExaminerNotesHistoryEditComponent],
- imports: [DynamicFormsModule, FormsModule, ReactiveFormsModule],
+ imports: [DynamicFormsModule, FormsModule, ReactiveFormsModule, RouterTestingModule],
providers: [
{ provide: TechnicalRecordService, useValue: mockTechRecordService },
+ provideMockStore({ initialState: initialAppState }),
+ { provide: ActivatedRoute, useValue: { params: of([{ id: 1 }]) } },
],
}).compileComponents();
fixture = TestBed.createComponent(AdrExaminerNotesHistoryEditComponent);
component = fixture.componentInstance;
+ router = TestBed.inject(Router);
});
describe('ngOnDestroy', () => {
it('should call destroy$.next and destroy$.complete', () => {
@@ -48,7 +60,7 @@ describe('AdrExaminerNotesHistoryEditComponent', () => {
component.currentTechRecord = mockVehicleTechnicalRecord(VehicleTypes.HGV) as TechRecordType<'hgv'>;
const testNote = {
note: 'testNote',
- createdAtDate: new Date().toISOString().split('T')[0],
+ createdAtDate: new Date().toISOString(),
lastUpdatedBy: 'Someone Somewhere',
};
component.currentTechRecord.techRecord_adrDetails_additionalExaminerNotes = [testNote];
@@ -56,4 +68,43 @@ describe('AdrExaminerNotesHistoryEditComponent', () => {
expect(notes).toEqual([testNote]);
});
});
+ describe('getEditAdditionalExaminerNotePage', () => {
+ it('should navigate you to the EditAdditionalExaminerNotePage', () => {
+ const routerSpy = jest.spyOn(router, 'navigate');
+ component.getEditAdditionalExaminerNotePage(1);
+ expect(routerSpy).toHaveBeenCalled();
+ });
+ });
+
+ describe('handlePaginationChange', () => {
+ it('should set the start and end pages', () => {
+ component.handlePaginationChange({ start: 0, end: 3 });
+
+ expect(component.pageStart).toBe(0);
+ expect(component.pageEnd).toBe(3);
+ });
+ });
+
+ describe('currentAdrNotesPage', () => {
+ it('should return a sliced array of adr notes depending on the page the user is on', () => {
+ component.currentTechRecord = { ...MOCK_HGV };
+ component.pageStart = 1;
+ component.pageEnd = 2;
+ component.currentTechRecord.techRecord_adrDetails_additionalExaminerNotes = [
+ { createdAtDate: 'test1', lastUpdatedBy: 'test1', note: 'test note 1' },
+ { createdAtDate: 'test2', lastUpdatedBy: 'test2', note: 'test note 2' },
+ ];
+ expect(component.currentAdrNotesPage).toEqual([
+ { createdAtDate: 'test2', lastUpdatedBy: 'test2', note: 'test note 2' },
+ ]);
+ });
+
+ it('should return an empty array if the adr examiner notes is undefined', () => {
+ component.currentTechRecord = { ...MOCK_HGV };
+ component.pageStart = 2;
+ component.pageEnd = 3;
+ component.currentTechRecord.techRecord_adrDetails_additionalExaminerNotes = undefined;
+ expect(component.currentAdrNotesPage).toEqual([]);
+ });
+ });
});
diff --git a/src/app/forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit.ts b/src/app/forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit.ts
index 608210e6da..08e212cf4e 100644
--- a/src/app/forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit.ts
+++ b/src/app/forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit.ts
@@ -1,4 +1,4 @@
-import { KeyValue } from '@angular/common';
+import { KeyValue, ViewportScroller } from '@angular/common';
import {
AfterContentInit,
Component, inject, OnDestroy, OnInit,
@@ -9,6 +9,12 @@ import { BaseControlComponent } from '@forms/components/base-control/base-contro
import { CustomControl, CustomFormControl } from '@forms/services/dynamic-form.types';
import { TechnicalRecordService } from '@services/technical-record/technical-record.service';
import { ReplaySubject, takeUntil } from 'rxjs';
+import { updateScrollPosition } from '@store/technical-records';
+import { TechnicalRecordServiceState } from '@store/technical-records/reducers/technical-record-service.reducer';
+import { Store } from '@ngrx/store';
+import { ActivatedRoute, Router } from '@angular/router';
+import { ReasonForEditing } from '@models/vehicle-tech-record.model';
+import { AdditionalExaminerNotes } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/hgv/complete';
@Component({
selector: 'app-adr-examiner-notes-history',
@@ -16,23 +22,29 @@ import { ReplaySubject, takeUntil } from 'rxjs';
styleUrls: ['adr-examiner-notes-history.component-edit.scss'],
})
export class AdrExaminerNotesHistoryEditComponent extends BaseControlComponent implements OnInit, OnDestroy, AfterContentInit {
-
destroy$ = new ReplaySubject(1);
-
formArray = new FormArray([]);
currentTechRecord?: TechRecordType<'hgv' | 'lgv' | 'trl'> = undefined;
technicalRecordService = inject(TechnicalRecordService);
+ store = inject(Store);
+ viewportScroller = inject(ViewportScroller);
+ router = inject(Router);
+ route = inject(ActivatedRoute);
+ editingReason?: ReasonForEditing;
+ pageStart?: number;
+ pageEnd?: number;
- ngOnInit() {
+ ngOnInit(): void {
this.formArray.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((changes) => {
this.control?.patchValue(changes, { emitModelToViewChange: true });
});
this.technicalRecordService.techRecord$.pipe(takeUntil(this.destroy$)).subscribe((currentTechRecord) => {
this.currentTechRecord = currentTechRecord as TechRecordType<'hgv' | 'lgv' | 'trl'>;
});
+ this.editingReason = this.route.snapshot.data['reason'];
}
- override ngAfterContentInit() {
+ override ngAfterContentInit(): void {
const injectedControl = this.injector.get(NgControl, null);
if (injectedControl) {
const ngControl = injectedControl.control as unknown as KeyValue;
@@ -43,14 +55,30 @@ export class AdrExaminerNotesHistoryEditComponent extends BaseControlComponent i
}
}
- getAdditionalExaminerNotes() {
- const returnValue = this.currentTechRecord ? this.currentTechRecord.techRecord_adrDetails_additionalExaminerNotes ?? [] : [];
- return returnValue;
+ handlePaginationChange({ start, end }: { start: number; end: number }): void {
+ this.pageStart = start;
+ this.pageEnd = end;
+ this.cdr.detectChanges();
+ }
+
+ getAdditionalExaminerNotes(): AdditionalExaminerNotes[] {
+ return this.currentTechRecord?.techRecord_adrDetails_additionalExaminerNotes ?? [];
+ }
+
+ get currentAdrNotesPage(): AdditionalExaminerNotes[] {
+ return this.currentTechRecord?.techRecord_adrDetails_additionalExaminerNotes?.slice(this.pageStart, this.pageEnd) ?? [];
}
- ngOnDestroy() {
+ getEditAdditionalExaminerNotePage(examinerNoteIndex: number) {
+ const route = `../${this.editingReason}/edit-additional-examiner-note/${examinerNoteIndex}`;
+
+ this.store.dispatch(updateScrollPosition({ position: this.viewportScroller.getScrollPosition() }));
+
+ void this.router.navigate([route], { relativeTo: this.route, state: this.currentTechRecord });
+ }
+
+ ngOnDestroy(): void {
this.destroy$.next(true);
this.destroy$.complete();
}
-
}
diff --git a/src/app/forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component.html b/src/app/forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component.html
index 6e640b7c6e..52d754749e 100644
--- a/src/app/forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component.html
+++ b/src/app/forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component.html
@@ -1,32 +1,45 @@
-
- ADR Examiner Notes History
- 0; else empty">
-
-
-
-
-
-
-
-
-
- {{ result.createdAtDate | date : 'dd/MM/yyyy' | defaultNullOrEmpty }}
- |
- {{ result.lastUpdatedBy }} |
-
- {{ result.note }}
- |
+
+
+ 0; else empty">
+
+
+
+
+
-
-
-
-
-
-
- - No additional examiner notes history available
-
-
-
-
+
+
+
+
+
+
+
+ {{ result.note }}
+
+ |
+ {{ result.lastUpdatedBy }} |
+
+ {{ result.createdAtDate | date: 'dd/MM/yyyy HH:mm' | defaultNullOrEmpty }}
+ |
+
+
+
+
+
+
+
+
+ - No additional examiner notes history available
+
+
+
+
+
diff --git a/src/app/forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component.spec.ts b/src/app/forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component.spec.ts
index 19bfc7d3d1..cac1bda6d0 100644
--- a/src/app/forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component.spec.ts
+++ b/src/app/forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component.spec.ts
@@ -3,12 +3,24 @@ import { NG_VALUE_ACCESSOR, NgControl } from '@angular/forms';
import { CustomFormControl, FormNodeTypes } from '@forms/services/dynamic-form.types';
import { provideMockStore } from '@ngrx/store/testing';
import { State, initialAppState } from '@store/index';
+import { TechnicalRecordService } from '@services/technical-record/technical-record.service';
+import { mockVehicleTechnicalRecord } from '@mocks/mock-vehicle-technical-record.mock';
+import { VehicleTypes } from '@models/vehicle-tech-record.model';
+import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type';
+import { of } from 'rxjs';
+import { RouterService } from '@services/router/router.service';
import { AdrExaminerNotesHistoryViewComponent } from './adr-examiner-notes-history-view.component';
describe('AdrExaminerNotesHistoryViewComponent', () => {
let component: AdrExaminerNotesHistoryViewComponent;
let fixture: ComponentFixture;
+ const MOCK_HGV = mockVehicleTechnicalRecord(VehicleTypes.HGV) as TechRecordType<'hgv'>;
+ const mockTechRecordService = {
+ techRecord$: of({ ...MOCK_HGV }),
+ };
+ const mockRouterService = {};
+
const control = new CustomFormControl({
name: 'techRecord_adrDetails_additionalExaminerNotes',
type: FormNodeTypes.CONTROL,
@@ -19,6 +31,8 @@ describe('AdrExaminerNotesHistoryViewComponent', () => {
declarations: [AdrExaminerNotesHistoryViewComponent],
providers: [
provideMockStore({ initialState: initialAppState }),
+ { provide: TechnicalRecordService, useValue: mockTechRecordService },
+ { provide: RouterService, useValue: mockRouterService },
{ provide: NG_VALUE_ACCESSOR, useExisting: AdrExaminerNotesHistoryViewComponent, multi: true },
{
provide: NgControl,
@@ -38,4 +52,63 @@ describe('AdrExaminerNotesHistoryViewComponent', () => {
it('should create', () => {
expect(component).toBeTruthy();
});
+
+ describe('ngOnInit', () => {
+ it('should set the currentTechRecord when ngOnInit is fired', () => {
+ component.currentTechRecord = undefined;
+
+ component.ngOnInit();
+ expect(component.currentTechRecord).toEqual(MOCK_HGV);
+ });
+ });
+
+ describe('adrNotes', () => {
+ it('should return an array of the technical records adr examiner notes', () => {
+ component.currentTechRecord = { ...MOCK_HGV };
+ component.currentTechRecord.techRecord_adrDetails_additionalExaminerNotes = [
+ { createdAtDate: 'test', lastUpdatedBy: 'test', note: 'test note' },
+ ];
+ expect(component.adrNotes).toEqual([{ createdAtDate: 'test', lastUpdatedBy: 'test', note: 'test note' }]);
+ });
+
+ it('should return an empty array if the adr examiner notes is undefined', () => {
+ component.currentTechRecord = { ...MOCK_HGV };
+ component.currentTechRecord.techRecord_adrDetails_additionalExaminerNotes = undefined;
+ expect(component.adrNotes).toEqual([]);
+ });
+ });
+
+ describe('currentAdrNotesPage', () => {
+ it('should return a sliced array of adr notes depending on the page the user is on', () => {
+ component.currentTechRecord = { ...MOCK_HGV };
+ component.pageStart = 2;
+ component.pageEnd = 3;
+ component.currentTechRecord.techRecord_adrDetails_additionalExaminerNotes = [
+ { createdAtDate: 'test1', lastUpdatedBy: 'test1', note: 'test note 1' },
+ { createdAtDate: 'test2', lastUpdatedBy: 'test2', note: 'test note 2' },
+ { createdAtDate: 'test3', lastUpdatedBy: 'test3', note: 'test note 3' },
+ { createdAtDate: 'test4', lastUpdatedBy: 'test4', note: 'test note 4' },
+ ];
+ expect(component.currentAdrNotesPage).toEqual([
+ { createdAtDate: 'test3', lastUpdatedBy: 'test3', note: 'test note 3' },
+ ]);
+ });
+
+ it('should return an empty array if the adr examiner notes is undefined', () => {
+ component.currentTechRecord = { ...MOCK_HGV };
+ component.pageStart = 1;
+ component.pageEnd = 2;
+ component.currentTechRecord.techRecord_adrDetails_additionalExaminerNotes = undefined;
+ expect(component.currentAdrNotesPage).toEqual([]);
+ });
+ });
+
+ describe('handlePaginationChange', () => {
+ it('should set the start and end pages', () => {
+ component.handlePaginationChange({ start: 0, end: 3 });
+
+ expect(component.pageStart).toBe(0);
+ expect(component.pageEnd).toBe(3);
+ });
+ });
});
diff --git a/src/app/forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component.ts b/src/app/forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component.ts
index 78fc2c0098..4e12d6f635 100644
--- a/src/app/forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component.ts
+++ b/src/app/forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component.ts
@@ -1,6 +1,15 @@
-import { Component } from '@angular/core';
+import {
+ Component, inject, OnDestroy, OnInit,
+} from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { BaseControlComponent } from '@forms/components/base-control/base-control.component';
+import { AdditionalExaminerNotes } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/hgv/complete';
+import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type';
+import { TechnicalRecordService } from '@services/technical-record/technical-record.service';
+import {
+ map, Observable, Subject, takeUntil,
+} from 'rxjs';
+import { RouterService } from '@services/router/router.service';
@Component({
selector: 'app-adr-examiner-notes-history-view',
@@ -14,5 +23,40 @@ import { BaseControlComponent } from '@forms/components/base-control/base-contro
},
],
})
-export class AdrExaminerNotesHistoryViewComponent extends BaseControlComponent {
+export class AdrExaminerNotesHistoryViewComponent extends BaseControlComponent implements OnInit, OnDestroy {
+ technicalRecordService = inject(TechnicalRecordService);
+ routerService = inject(RouterService);
+ currentTechRecord?: TechRecordType<'hgv' | 'lgv' | 'trl'> | undefined;
+ pageStart?: number;
+ pageEnd?: number;
+ private destroy$: Subject = new Subject();
+
+ ngOnInit(): void {
+ this.technicalRecordService.techRecord$.pipe(takeUntil(this.destroy$)).subscribe((currentTechRecord) => {
+ this.currentTechRecord = currentTechRecord as TechRecordType<'hgv' | 'lgv' | 'trl'>;
+ });
+ }
+
+ get isEditing$(): Observable {
+ return this.routerService.getRouteDataProperty$('isEditing').pipe(map((isEditing) => !!isEditing));
+ }
+
+ handlePaginationChange({ start, end }: { start: number; end: number }): void {
+ this.pageStart = start;
+ this.pageEnd = end;
+ this.cdr.detectChanges();
+ }
+
+ get adrNotes(): AdditionalExaminerNotes[] {
+ return this.currentTechRecord?.techRecord_adrDetails_additionalExaminerNotes ?? [];
+ }
+
+ get currentAdrNotesPage(): AdditionalExaminerNotes[] {
+ return this.currentTechRecord?.techRecord_adrDetails_additionalExaminerNotes?.slice(this.pageStart, this.pageEnd) ?? [];
+ }
+
+ ngOnDestroy(): void {
+ this.destroy$.next();
+ this.destroy$.complete();
+ }
}
diff --git a/src/app/forms/custom-sections/adr-guidance-notes/adr-guidance-notes.component.html b/src/app/forms/custom-sections/adr-guidance-notes/adr-guidance-notes.component.html
deleted file mode 100644
index 07d172802b..0000000000
--- a/src/app/forms/custom-sections/adr-guidance-notes/adr-guidance-notes.component.html
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
-
-
-
-
- 1">
-
-
-
-
-
- |
-
- Remove
- |
-
-
-
-
-
-
- Add guidance notes
-
-
diff --git a/src/app/forms/custom-sections/adr-guidance-notes/adr-guidance-notes.component.spec.ts b/src/app/forms/custom-sections/adr-guidance-notes/adr-guidance-notes.component.spec.ts
deleted file mode 100644
index 9e2a91411f..0000000000
--- a/src/app/forms/custom-sections/adr-guidance-notes/adr-guidance-notes.component.spec.ts
+++ /dev/null
@@ -1,126 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import {
- FormControl,
- FormGroup,
- FormsModule, NG_VALUE_ACCESSOR, NgControl, ReactiveFormsModule,
-} from '@angular/forms';
-import { FORM_INJECTION_TOKEN } from '@forms/components/dynamic-form-field/dynamic-form-field.component';
-import { FieldErrorMessageComponent } from '@forms/components/field-error-message/field-error-message.component';
-import { SelectComponent } from '@forms/components/select/select.component';
-import { DynamicFormsModule } from '@forms/dynamic-forms.module';
-import { CustomFormControl, FormNodeTypes } from '@forms/services/dynamic-form.types';
-import { AdrGuidanceNotesComponent } from './adr-guidance-notes.component';
-
-describe('AdrGuidanceNotesComponent', () => {
- let component: AdrGuidanceNotesComponent;
- let fixture: ComponentFixture;
-
- const control = new CustomFormControl({
- name: 'techRecord_adrDetails_additionalNotes_guidanceNotes',
- label: 'Guidance Notes',
- type: FormNodeTypes.CONTROL,
- });
-
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- declarations: [AdrGuidanceNotesComponent, SelectComponent, FieldErrorMessageComponent],
- imports: [DynamicFormsModule, FormsModule, ReactiveFormsModule],
- providers: [
- { provide: NG_VALUE_ACCESSOR, useExisting: AdrGuidanceNotesComponent, multi: true },
- {
- provide: NgControl,
- useValue: {
- control: { key: control.meta.name, value: control },
- },
- },
- {
- provide: FORM_INJECTION_TOKEN,
- useValue: new FormGroup({
- techRecord_adrDetails_additionalNotes_guidanceNotes: new FormControl(null),
- }),
- },
- ],
- })
- .compileComponents();
-
- fixture = TestBed.createComponent(AdrGuidanceNotesComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- beforeEach(() => {
- component.formArray.patchValue([]);
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-
- describe('ngOnInit', () => {
- it('should set a subscription to listen to form array changes, and patch the control value', () => {
- const ngOnInitSpy = jest.spyOn(component, 'ngOnInit');
- component.ngOnInit();
- expect(ngOnInitSpy).toHaveBeenCalled();
- });
- });
-
- describe('ngAfterContentInit', () => {
-
- it('should set the form control name', () => {
- component.ngAfterContentInit();
- expect(component.name).toBeTruthy();
- });
-
- it('should inject the form to which the control belongs', () => {
- component.ngAfterContentInit();
- expect(component.control).toBeTruthy();
- });
-
- it('should inject the control created by the dynamic form service, which is derrived from the template', () => {
- component.ngAfterContentInit();
- expect(component.control).toBeTruthy();
- });
-
- it('should make this control the first index of the form array', () => {
- const formArraySpy = jest.spyOn(component.formArray, 'push');
- component.ngAfterContentInit();
- expect(formArraySpy).toHaveBeenCalledTimes(1);
- expect(component.formArray.controls.at(0)).toBeDefined();
- });
- });
-
- describe('addGuidanceNote', () => {
- it('should add a copy of the first guidance control note to the form array (but with a reset value)', () => {
- const methodSpy = jest.spyOn(component, 'addGuidanceNote');
- const formArraySpy = jest.spyOn(component.formArray, 'push');
- component.addGuidanceNote();
- expect(methodSpy).toHaveBeenCalled();
- expect(formArraySpy).toHaveBeenCalled();
- expect(component.formArray.controls.at(1)).toBeDefined();
- expect(component.formArray.controls.at(1)?.value).toBeFalsy();
- });
- });
-
- describe('removeGuidanceNote', () => {
- it('should remove the guidance note control from the form array at the index specified', () => {
- const methodSpy = jest.spyOn(component, 'removeGuidanceNote');
- const formArraySpy = jest.spyOn(component.formArray, 'removeAt');
-
- component.addGuidanceNote();
- component.addGuidanceNote();
- component.addGuidanceNote();
- component.addGuidanceNote();
-
- component.removeGuidanceNote(3);
- component.removeGuidanceNote(2);
-
- expect(methodSpy).toHaveBeenCalledTimes(2);
- expect(formArraySpy).toHaveBeenCalledTimes(2);
- expect(component.formArray.controls.at(4)).toBeUndefined();
- expect(component.formArray.controls.at(3)).toBeUndefined();
- expect(component.formArray.controls.at(2)).toBeDefined();
- expect(component.formArray.controls).toHaveLength(3);
- });
- });
-});
diff --git a/src/app/forms/custom-sections/adr-guidance-notes/adr-guidance-notes.component.ts b/src/app/forms/custom-sections/adr-guidance-notes/adr-guidance-notes.component.ts
deleted file mode 100644
index 07641692cb..0000000000
--- a/src/app/forms/custom-sections/adr-guidance-notes/adr-guidance-notes.component.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-import {
- AfterContentInit,
- Component, OnDestroy, OnInit,
-} from '@angular/core';
-import {
- FormArray,
- NG_VALUE_ACCESSOR,
-} from '@angular/forms';
-import { CustomFormControl } from '@forms/services/dynamic-form.types';
-import { ReplaySubject, takeUntil } from 'rxjs';
-import { CustomFormControlComponent } from '../custom-form-control/custom-form-control.component';
-
-@Component({
- selector: 'app-adr-guidance-notes',
- templateUrl: './adr-guidance-notes.component.html',
- styleUrls: ['./adr-guidance-notes.component.scss'],
- providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: AdrGuidanceNotesComponent, multi: true }],
-})
-export class AdrGuidanceNotesComponent extends CustomFormControlComponent implements OnInit, AfterContentInit, OnDestroy {
- destroy$ = new ReplaySubject(1);
- formArray = new FormArray([]);
-
- ngOnInit() {
- this.formArray.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((changes) => {
- this.control?.patchValue(changes, { emitModelToViewChange: true });
- });
- }
-
- ngOnDestroy(): void {
- this.destroy$.next(true);
- this.destroy$.complete();
- }
-
- override ngAfterContentInit() {
- super.ngAfterContentInit();
- const { form, control } = this;
-
- if (!form) return;
- if (!control) return;
-
- const value = form.get(this.name)?.value;
- const values = Array.isArray(value) && value.length ? value : [null];
- values.forEach((guidanceNoteType: string) => {
- this.formArray.push(new CustomFormControl(control.meta, guidanceNoteType));
- });
- }
-
- addGuidanceNote() {
- const first = this.formArray.at(0);
- this.formArray.push(new CustomFormControl(first.meta));
- }
-
- removeGuidanceNote(index: number) {
- if (this.formArray.length < 2) return;
- this.formArray.removeAt(index);
- }
-
-}
diff --git a/src/app/forms/custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component.html b/src/app/forms/custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component.html
new file mode 100644
index 0000000000..45022e3b8c
--- /dev/null
+++ b/src/app/forms/custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ New Certificate required:
+ |
+ {{ control.value | defaultNullOrEmpty }} |
+
+
+
+
diff --git a/src/app/forms/custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component.scss b/src/app/forms/custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component.scss
new file mode 100644
index 0000000000..20740fe733
--- /dev/null
+++ b/src/app/forms/custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component.scss
@@ -0,0 +1,11 @@
+.m-0 {
+ margin: 0;
+}
+
+.break-words {
+ word-break: break-word;
+}
+
+.border-b-0 {
+ border-bottom-width: 0;
+}
\ No newline at end of file
diff --git a/src/app/forms/custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component.spec.ts b/src/app/forms/custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component.spec.ts
new file mode 100644
index 0000000000..88ca0a36d5
--- /dev/null
+++ b/src/app/forms/custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component.spec.ts
@@ -0,0 +1,41 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { NG_VALUE_ACCESSOR, NgControl } from '@angular/forms';
+import { CustomFormControl, FormNodeTypes } from '@forms/services/dynamic-form.types';
+import { provideMockStore } from '@ngrx/store/testing';
+import { State, initialAppState } from '@store/index';
+import { AdrTankDetailsM145ViewComponent } from '../adr-tank-details-m145-view/adr-tank-details-m145-view.component';
+import { AdrNewCertificateRequiredViewComponent } from './adr-new-certificate-required-view.component';
+import { SharedModule } from '@shared/shared.module';
+
+describe('AdrNewCertificateRequiredViewComponent', () => {
+ let component: AdrNewCertificateRequiredViewComponent;
+ let fixture: ComponentFixture;
+
+ const control = new CustomFormControl({
+ name: 'techRecord_adrDetails_m145Statement',
+ type: FormNodeTypes.CONTROL,
+ value: [],
+ });
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [AdrNewCertificateRequiredViewComponent],
+ imports: [SharedModule],
+ providers: [
+ provideMockStore({ initialState: initialAppState }),
+ { provide: NG_VALUE_ACCESSOR, useExisting: AdrTankDetailsM145ViewComponent, multi: true },
+ { provide: NgControl, useValue: { control } },
+ ],
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(AdrNewCertificateRequiredViewComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/forms/custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component.ts b/src/app/forms/custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component.ts
new file mode 100644
index 0000000000..132dcd90de
--- /dev/null
+++ b/src/app/forms/custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component.ts
@@ -0,0 +1,17 @@
+import { Component } from '@angular/core';
+import { NG_VALUE_ACCESSOR } from '@angular/forms';
+import { BaseControlComponent } from '@forms/components/base-control/base-control.component';
+
+@Component({
+ selector: 'app-adr-new-certificate-required-view',
+ templateUrl: './adr-new-certificate-required-view.component.html',
+ styleUrl: './adr-new-certificate-required-view.component.scss',
+ providers: [
+ {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: AdrNewCertificateRequiredViewComponent,
+ multi: true,
+ },
+ ],
+})
+export class AdrNewCertificateRequiredViewComponent extends BaseControlComponent {}
diff --git a/src/app/forms/dynamic-forms.module.ts b/src/app/forms/dynamic-forms.module.ts
index 306df05dec..a9aea0b22a 100644
--- a/src/app/forms/dynamic-forms.module.ts
+++ b/src/app/forms/dynamic-forms.module.ts
@@ -38,7 +38,9 @@ import { ViewCombinationComponent } from './components/view-combination/view-com
import { ViewListItemComponent } from './components/view-list-item/view-list-item.component';
import { AbandonDialogComponent } from './custom-sections/abandon-dialog/abandon-dialog.component';
import { AdrExaminerNotesHistoryViewComponent } from './custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component';
-import { AdrGuidanceNotesComponent } from './custom-sections/adr-guidance-notes/adr-guidance-notes.component';
+import {
+ AdrNewCertificateRequiredViewComponent,
+} from './custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component';
import {
AdrTankDetailsInitialInspectionViewComponent,
} from './custom-sections/adr-tank-details-initial-inspection-view/adr-tank-details-initial-inspection-view.component';
@@ -131,7 +133,6 @@ import { SuffixDirective } from './directives/suffix.directive';
ModifiedWeightsComponent,
FieldWarningMessageComponent,
AdrComponent,
- AdrGuidanceNotesComponent,
AdrTankDetailsSubsequentInspectionsEditComponent,
AdrTankStatementUnNumberEditComponent,
CustomFormControlComponent,
@@ -143,6 +144,7 @@ import { SuffixDirective } from './directives/suffix.directive';
AdrCertificateHistoryComponent,
AdrTankDetailsM145ViewComponent,
ContingencyAdrGenerateCertComponent,
+ AdrNewCertificateRequiredViewComponent,
],
imports: [CommonModule, FormsModule, ReactiveFormsModule, SharedModule, RouterModule],
exports: [
@@ -192,6 +194,7 @@ import { SuffixDirective } from './directives/suffix.directive';
ModifiedWeightsComponent,
AdrComponent,
AdrCertificateHistoryComponent,
+ FieldWarningMessageComponent,
],
})
export class DynamicFormsModule { }
diff --git a/src/app/forms/models/async-validators.enum.ts b/src/app/forms/models/async-validators.enum.ts
index 14985ee9ca..4232bbe6ff 100644
--- a/src/app/forms/models/async-validators.enum.ts
+++ b/src/app/forms/models/async-validators.enum.ts
@@ -9,4 +9,5 @@ export enum AsyncValidatorNames {
RequiredIfNotResult = 'requiredIfNotResult',
HideIfEqualsWithCondition = 'hideIfEqualsWithCondition',
PassResultDependantOnCustomDefects = 'passResultDependantOnCustomDefects',
+ RequiredWhenCarryingDangerousGoods = 'requiredWhenCarryingDangerousGoods',
}
diff --git a/src/app/forms/services/dynamic-form.service.ts b/src/app/forms/services/dynamic-form.service.ts
index 0cb554a6e7..719ca039cc 100644
--- a/src/app/forms/services/dynamic-form.service.ts
+++ b/src/app/forms/services/dynamic-form.service.ts
@@ -107,6 +107,7 @@ export class DynamicFormService {
[AsyncValidatorNames.ResultDependantOnRequiredStandards]: () => CustomAsyncValidators.resultDependantOnRequiredStandards(this.store),
[AsyncValidatorNames.UpdateTesterDetails]: () => CustomAsyncValidators.updateTesterDetails(this.store),
[AsyncValidatorNames.UpdateTestStationDetails]: () => CustomAsyncValidators.updateTestStationDetails(this.store),
+ [AsyncValidatorNames.RequiredWhenCarryingDangerousGoods]: () => CustomAsyncValidators.requiredWhenCarryingDangerousGoods(this.store),
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
diff --git a/src/app/forms/templates/general/adr-summary.template.ts b/src/app/forms/templates/general/adr-summary.template.ts
index d54e01d5d1..3ef290f770 100644
--- a/src/app/forms/templates/general/adr-summary.template.ts
+++ b/src/app/forms/templates/general/adr-summary.template.ts
@@ -14,11 +14,13 @@ import {
import {
AdrExaminerNotesHistoryViewComponent,
} from '@forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component';
-import { AdrGuidanceNotesComponent } from '@forms/custom-sections/adr-guidance-notes/adr-guidance-notes.component';
-import { AdrTankDetailsM145ViewComponent } from '@forms/custom-sections/adr-tank-details-m145-view/adr-tank-details-m145-view.component';
+import {
+ AdrNewCertificateRequiredViewComponent,
+} from '@forms/custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component';
import {
AdrTankDetailsInitialInspectionViewComponent,
} from '@forms/custom-sections/adr-tank-details-initial-inspection-view/adr-tank-details-initial-inspection-view.component';
+import { AdrTankDetailsM145ViewComponent } from '@forms/custom-sections/adr-tank-details-m145-view/adr-tank-details-m145-view.component';
import {
AdrTankDetailsSubsequentInspectionsEditComponent,
} from '@forms/custom-sections/adr-tank-details-subsequent-inspections-edit/adr-tank-details-subsequent-inspections-edit.component';
@@ -171,6 +173,18 @@ export const AdrSummaryTemplate: FormNode = {
},
],
},
+ {
+ name: 'techRecord_adrDetails_vehicleDetails_usedOnInternationalJourneys',
+ label: 'Vehicle used on international journeys',
+ type: FormNodeTypes.CONTROL,
+ options: [
+ { value: 'yes', label: 'Yes' },
+ { value: 'no', label: 'No' },
+ { value: 'n/a', label: 'Not applicable' },
+ ],
+ hide: true,
+ groups: ['adr_details', 'dangerous_goods'],
+ },
{
name: 'techRecord_adrDetails_vehicleDetails_approvalDate',
label: 'Date processed',
@@ -236,20 +250,13 @@ export const AdrSummaryTemplate: FormNode = {
name: 'techRecord_adrDetails_additionalNotes_number',
label: 'Guidance notes',
type: FormNodeTypes.CONTROL,
- editType: FormNodeEditTypes.CUSTOM,
- editComponent: AdrGuidanceNotesComponent,
+ editType: FormNodeEditTypes.CHECKBOXGROUP,
groups: ['adr_details', 'dangerous_goods'],
hide: true,
width: FormNodeWidth.XS,
value: [],
- customErrorMessage: 'Guidance notes is required with Able to carry dangerous goods',
options: getOptionsFromEnum(ADRAdditionalNotesNumber),
- validators: [
- {
- name: ValidatorNames.IsArray,
- args: { requiredIndices: [0], whenEquals: { sibling: 'techRecord_adrDetails_dangerousGoods', value: [true] } },
- },
- ],
+ validators: [],
},
{
name: 'techRecord_adrDetails_adrTypeApprovalNo',
@@ -757,6 +764,31 @@ export const AdrSummaryTemplate: FormNode = {
groups: ['declarations_details', 'dangerous_goods'],
hide: true,
},
+ {
+ name: 'NewCertificateRequested',
+ label: 'New Certificate required',
+ type: FormNodeTypes.TITLE,
+ groups: ['dangerous_goods'],
+ hide: true,
+ },
+ {
+ name: 'techRecord_adrDetails_newCertificateRequested',
+ label: 'Yes',
+ type: FormNodeTypes.CONTROL,
+ editType: FormNodeEditTypes.CHECKBOX,
+ viewType: FormNodeViewTypes.CUSTOM,
+ viewComponent: AdrNewCertificateRequiredViewComponent,
+ value: false,
+ groups: ['dangerous_goods'],
+ hide: true,
+ },
+ {
+ name: 'ExaminerNotesSectionTitle',
+ label: 'Additional Examiner Notes History',
+ type: FormNodeTypes.TITLE,
+ groups: ['adr_details', 'dangerous_goods'],
+ hide: true,
+ },
{
name: 'techRecord_adrDetails_additionalExaminerNotes_note',
label: 'Additional Examiner Notes',
@@ -774,7 +806,7 @@ export const AdrSummaryTemplate: FormNode = {
label: 'Additional examiner notes history',
value: null,
type: FormNodeTypes.CONTROL,
- viewType: FormNodeViewTypes.HIDDEN,
+ viewType: FormNodeViewTypes.CUSTOM,
viewComponent: AdrExaminerNotesHistoryViewComponent,
editType: FormNodeEditTypes.CUSTOM,
editComponent: AdrExaminerNotesHistoryEditComponent,
diff --git a/src/app/forms/templates/general/adr.template.ts b/src/app/forms/templates/general/adr.template.ts
index 4cc047312e..2ea993c830 100644
--- a/src/app/forms/templates/general/adr.template.ts
+++ b/src/app/forms/templates/general/adr.template.ts
@@ -18,11 +18,15 @@ import {
import {
AdrExaminerNotesHistoryViewComponent,
} from '@forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component';
-import { AdrGuidanceNotesComponent } from '@forms/custom-sections/adr-guidance-notes/adr-guidance-notes.component';
+import {
+ AdrNewCertificateRequiredViewComponent,
+} from '@forms/custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component';
import {
AdrTankDetailsInitialInspectionViewComponent,
} from '@forms/custom-sections/adr-tank-details-initial-inspection-view/adr-tank-details-initial-inspection-view.component';
-import { AdrTankDetailsM145ViewComponent } from '@forms/custom-sections/adr-tank-details-m145-view/adr-tank-details-m145-view.component';
+import {
+ AdrTankDetailsM145ViewComponent,
+} from '@forms/custom-sections/adr-tank-details-m145-view/adr-tank-details-m145-view.component';
import {
AdrTankDetailsSubsequentInspectionsEditComponent,
} from '@forms/custom-sections/adr-tank-details-subsequent-inspections-edit/adr-tank-details-subsequent-inspections-edit.component';
@@ -176,6 +180,19 @@ export const AdrTemplate: FormNode = {
},
],
},
+ {
+ name: 'techRecord_adrDetails_vehicleDetails_usedOnInternationalJourneys',
+ label: 'Vehicle used on international journeys',
+ type: FormNodeTypes.CONTROL,
+ editType: FormNodeEditTypes.RADIO,
+ options: [
+ { value: 'yes', label: 'Yes' },
+ { value: 'no', label: 'No' },
+ { value: 'n/a', label: 'Not applicable' },
+ ],
+ hide: true,
+ groups: ['adr_details', 'dangerous_goods'],
+ },
{
name: 'techRecord_adrDetails_vehicleDetails_approvalDate',
label: 'Date processed',
@@ -242,19 +259,14 @@ export const AdrTemplate: FormNode = {
name: 'techRecord_adrDetails_additionalNotes_number',
label: 'Guidance notes',
type: FormNodeTypes.CONTROL,
- editType: FormNodeEditTypes.CUSTOM,
- editComponent: AdrGuidanceNotesComponent,
+ editType: FormNodeEditTypes.CHECKBOXGROUP,
groups: ['adr_details', 'dangerous_goods'],
hide: true,
width: FormNodeWidth.XS,
value: [],
- customErrorMessage: 'Guidance notes is required with Able to carry dangerous goods',
options: getOptionsFromEnum(ADRAdditionalNotesNumber),
validators: [
- {
- name: ValidatorNames.IsArray,
- args: { requiredIndices: [0], whenEquals: { sibling: 'techRecord_adrDetails_dangerousGoods', value: [true] } },
- },
+ { name: ValidatorNames.RequiredIfEquals, args: { sibling: 'techRecord_adrDetails_dangerousGoods', value: [true] } },
],
},
{
@@ -587,6 +599,7 @@ export const AdrTemplate: FormNode = {
{
name: 'techRecord_adrDetails_memosApply',
label: 'Memo 7/9 (3 month extension) applied',
+ hint: 'Only applicable for vehicles used on national journeys',
type: FormNodeTypes.CONTROL,
editType: FormNodeEditTypes.CHECKBOXGROUP,
groups: ['tank_details', 'dangerous_goods'],
@@ -756,6 +769,31 @@ export const AdrTemplate: FormNode = {
groups: ['declarations_details', 'dangerous_goods'],
hide: true,
},
+ {
+ name: 'NewCertificateRequested',
+ label: 'New Certificate required',
+ type: FormNodeTypes.TITLE,
+ groups: ['dangerous_goods'],
+ hide: true,
+ },
+ {
+ name: 'techRecord_adrDetails_newCertificateRequested',
+ label: 'Yes',
+ type: FormNodeTypes.CONTROL,
+ viewType: FormNodeViewTypes.CUSTOM,
+ viewComponent: AdrNewCertificateRequiredViewComponent,
+ editType: FormNodeEditTypes.CHECKBOX,
+ value: false,
+ groups: ['dangerous_goods'],
+ hide: true,
+ },
+ {
+ name: 'ExaminerNotesSectionTitle',
+ label: 'Additional Examiner Notes History',
+ type: FormNodeTypes.TITLE,
+ groups: ['adr_details', 'dangerous_goods'],
+ hide: true,
+ },
{
name: 'techRecord_adrDetails_additionalExaminerNotes_note',
label: 'Additional Examiner Notes',
@@ -775,7 +813,7 @@ export const AdrTemplate: FormNode = {
label: 'Additional examiner notes history',
value: null,
type: FormNodeTypes.CONTROL,
- viewType: FormNodeViewTypes.ADR_EXAMINER_NOTES, // TODO: replace with custom
+ viewType: FormNodeViewTypes.CUSTOM,
viewComponent: AdrExaminerNotesHistoryViewComponent,
editType: FormNodeEditTypes.CUSTOM,
editComponent: AdrExaminerNotesHistoryEditComponent,
diff --git a/src/app/forms/templates/general/hgv-trl-body.template.ts b/src/app/forms/templates/general/hgv-trl-body.template.ts
index eda37032a8..e097b75124 100644
--- a/src/app/forms/templates/general/hgv-trl-body.template.ts
+++ b/src/app/forms/templates/general/hgv-trl-body.template.ts
@@ -1,3 +1,4 @@
+import { AsyncValidatorNames } from '@forms/models/async-validators.enum';
import { ValidatorNames } from '@forms/models/validators.enum';
import { TagType } from '@shared/components/tag/tag.component';
import {
@@ -27,6 +28,7 @@ export const HgvAndTrlBodyTemplate: FormNode = {
type: FormNodeTypes.CONTROL,
editType: FormNodeEditTypes.TEXT,
validators: [{ name: ValidatorNames.MaxLength, args: 50 }],
+ asyncValidators: [{ name: AsyncValidatorNames.RequiredWhenCarryingDangerousGoods }],
customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }],
},
{
diff --git a/src/app/forms/utils/error-message-map.ts b/src/app/forms/utils/error-message-map.ts
index 9a47414b52..8318a8007f 100644
--- a/src/app/forms/utils/error-message-map.ts
+++ b/src/app/forms/utils/error-message-map.ts
@@ -50,4 +50,5 @@ export const ErrorMessageMap: Record = {
[AsyncValidatorNames.RequiredIfNotFail]: (err: boolean, label?: string) => `${label || DEFAULT_LABEL} is required`,
[AsyncValidatorNames.RequiredIfNotResult]: (err: boolean, label?: string) => `${label || DEFAULT_LABEL} is required`,
[AsyncValidatorNames.RequiredIfNotResultAndSiblingEquals]: (err: boolean, label?: string) => `${label || DEFAULT_LABEL} is required`,
+ [AsyncValidatorNames.RequiredWhenCarryingDangerousGoods]: (err: { message: string }) => err.message,
};
diff --git a/src/app/forms/validators/custom-async-validators.spec.ts b/src/app/forms/validators/custom-async-validators.spec.ts
index 11f29ebf81..6c4e919959 100644
--- a/src/app/forms/validators/custom-async-validators.spec.ts
+++ b/src/app/forms/validators/custom-async-validators.spec.ts
@@ -1,5 +1,6 @@
import { TestBed } from '@angular/core/testing';
-import { FormGroup, ValidationErrors } from '@angular/forms';
+import { AbstractControl, FormGroup, ValidationErrors } from '@angular/forms';
+import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb-vehicle-type';
import { operatorEnum } from '@forms/models/condition.model';
import { CustomFormControl, FormNodeTypes } from '@forms/services/dynamic-form.types';
import { createMockCustomDefect } from '@mocks/custom-defect.mock';
@@ -8,9 +9,10 @@ import { TestResultModel } from '@models/test-results/test-result.model';
import { resultOfTestEnum } from '@models/test-types/test-type.model';
import { MockStore, provideMockStore } from '@ngrx/store/testing';
import { State, initialAppState } from '@store/.';
+import { editingTechRecord } from '@store/technical-records';
import { testResultInEdit } from '@store/test-records';
import { initialTestStationsState } from '@store/test-stations';
-import { Observable, firstValueFrom } from 'rxjs';
+import { Observable, firstValueFrom, lastValueFrom } from 'rxjs';
import { CustomAsyncValidators } from './custom-async-validators';
describe('resultDependantOnCustomDefects', () => {
@@ -696,3 +698,121 @@ describe('hide if equals with condition', () => {
expect((form.controls['bar'] as CustomFormControl).meta.hide).toBe(false);
});
});
+
+describe('requiredWhenCarryingDangerousGoods', () => {
+ let form: FormGroup;
+ let store: MockStore;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ providers: [provideMockStore({ initialState: initialAppState })],
+ });
+
+ store = TestBed.inject(MockStore);
+
+ form = new FormGroup({
+ techRecord_make: new CustomFormControl({
+ name: 'techRecord_make',
+ type: FormNodeTypes.CONTROL,
+ children: [],
+ }, null),
+ techRecord_adrDetails_dangerousGoods: new CustomFormControl({
+ name: 'techRecord_adrDetails_dangerousGoods',
+ type: FormNodeTypes.CONTROL,
+ children: [],
+ }, null),
+ });
+ });
+ it('should return null if the vehicle is not of type HGV or TRL', async () => {
+ // Not applicable tech record vehicle type
+ const carTechRecord: TechRecordType<'car', 'put'> = {
+ techRecord_vehicleType: 'car',
+ vin: 'car',
+ techRecord_reasonForCreation: 'test',
+ techRecord_statusCode: 'provisional',
+ };
+
+ store.overrideSelector(editingTechRecord, carTechRecord);
+
+ const result = CustomAsyncValidators.requiredWhenCarryingDangerousGoods(store)(form.get('techRecord_make') as AbstractControl);
+ await expect(lastValueFrom(result)).resolves.toBeNull();
+ });
+
+ it('should return null if the control is populated', async () => {
+ // Applicable vehicle tech record type
+ const hgvTechRecord: TechRecordType<'hgv', 'put'> = {
+ techRecord_vehicleType: 'hgv',
+ partialVin: '',
+ techRecord_bodyType_description: '',
+ techRecord_noOfAxles: 2,
+ techRecord_reasonForCreation: 'test',
+ techRecord_statusCode: 'provisional',
+ techRecord_vehicleClass_description: 'heavy goods vehicle',
+ primaryVrm: '',
+ vin: '',
+
+ // Vehicle does carry dangerous goods
+ techRecord_adrDetails_dangerousGoods: true,
+ };
+
+ // ...but the control is populated
+ form.get('techRecord_make')?.patchValue('make');
+
+ store.overrideSelector(editingTechRecord, hgvTechRecord);
+
+ const result = CustomAsyncValidators.requiredWhenCarryingDangerousGoods(store)(form.get('techRecord_make') as AbstractControl);
+ await expect(lastValueFrom(result)).resolves.toBeNull();
+ });
+
+ it('should return null if the vehicle is an ADR vehicle, but does not carry dangerous goods', async () => {
+ // Applicable vehicle tech record type
+ const hgvTechRecord: TechRecordType<'hgv', 'put'> = {
+ techRecord_vehicleType: 'hgv',
+ partialVin: '',
+ techRecord_bodyType_description: '',
+ techRecord_noOfAxles: 2,
+ techRecord_reasonForCreation: 'test',
+ techRecord_statusCode: 'provisional',
+ techRecord_vehicleClass_description: 'heavy goods vehicle',
+ primaryVrm: '',
+ vin: '',
+
+ // Vehicle doesn't carry dangerous goods
+ techRecord_adrDetails_dangerousGoods: false,
+ };
+
+ // ...and the control isn't populated
+ form.get('techRecord_make')?.patchValue(null);
+
+ store.overrideSelector(editingTechRecord, hgvTechRecord);
+
+ const result = CustomAsyncValidators.requiredWhenCarryingDangerousGoods(store)(form.get('techRecord_make') as AbstractControl);
+ await expect(lastValueFrom(result)).resolves.toBeNull();
+ });
+
+ it('should return the required validation error when the vehicle is an ADR vehicle, and carries dangerous goods is selected', async () => {
+ // Applicable vehicle tech record type
+ const hgvTechRecord: TechRecordType<'hgv', 'put'> = {
+ techRecord_vehicleType: 'hgv',
+ partialVin: '',
+ techRecord_bodyType_description: '',
+ techRecord_noOfAxles: 2,
+ techRecord_reasonForCreation: 'test',
+ techRecord_statusCode: 'provisional',
+ techRecord_vehicleClass_description: 'heavy goods vehicle',
+ primaryVrm: '',
+ vin: '',
+
+ // Vehicle does carry dangerous goods
+ techRecord_adrDetails_dangerousGoods: true,
+ };
+
+ // ...and the control isn't populated
+ form.get('techRecord_make')?.patchValue(null);
+
+ store.overrideSelector(editingTechRecord, hgvTechRecord);
+
+ const result = CustomAsyncValidators.requiredWhenCarryingDangerousGoods(store)(form.get('techRecord_make') as AbstractControl);
+ await expect(lastValueFrom(result)).resolves.toStrictEqual({ required: true });
+ });
+});
diff --git a/src/app/forms/validators/custom-async-validators.ts b/src/app/forms/validators/custom-async-validators.ts
index 1a0b1ad744..395ed33793 100644
--- a/src/app/forms/validators/custom-async-validators.ts
+++ b/src/app/forms/validators/custom-async-validators.ts
@@ -1,4 +1,6 @@
-import { AbstractControl, AsyncValidatorFn, ValidationErrors } from '@angular/forms';
+import {
+ AbstractControl, AsyncValidatorFn, ValidationErrors, Validators,
+} from '@angular/forms';
import { Condition, operatorEnum } from '@forms/models/condition.model';
// eslint-disable-next-line import/no-cycle
import { CustomFormControl } from '@forms/services/dynamic-form.types';
@@ -9,6 +11,7 @@ import { resultOfTestEnum } from '@models/test-types/test-type.model';
import { Store, select } from '@ngrx/store';
import { State } from '@store/.';
import { selectUserByResourceKey } from '@store/reference-data';
+import { editingTechRecord } from '@store/technical-records';
import { testResultInEdit } from '@store/test-records';
import { getTestStationFromProperty } from '@store/test-stations';
import {
@@ -224,6 +227,21 @@ export class CustomAsyncValidators {
);
}
+ static requiredWhenCarryingDangerousGoods = (store: Store) => {
+ return (control: AbstractControl): Observable => {
+ return store.select(editingTechRecord).pipe(take(1), map((form) => {
+ if (
+ form
+ && (form.techRecord_vehicleType === 'hgv' || form.techRecord_vehicleType === 'trl')
+ && (form.techRecord_adrDetails_dangerousGoods)) {
+ return Validators.required(control);
+ }
+
+ return null;
+ }));
+ };
+ };
+
private static checkConditions(testResult: TestResultModel, conditions: Condition | Condition[]) {
if (!Array.isArray(conditions)) {
return CustomAsyncValidators.checkCondition(testResult, conditions);
diff --git a/src/app/models/routes.enum.ts b/src/app/models/routes.enum.ts
index f478b7c7d4..1df9e718a1 100644
--- a/src/app/models/routes.enum.ts
+++ b/src/app/models/routes.enum.ts
@@ -30,8 +30,10 @@ export enum TechRecordRoutes {
CHANGE_VTA_VISIBILITY = 'change-vta-visibility',
CORRECT_ERROR_TYRE_SEARCH = 'correcting-an-error/tyre-search/:axleNumber',
CORRECT_ERROR_CHANGE_SUMMARY = 'correcting-an-error/change-summary',
+ CORRECT_ERROR_EDIT_ADDITIONAL_EXAMINER_NOTE = 'correcting-an-error/edit-additional-examiner-note/:examinerNoteIndex',
NOTIFIABLE_ALTERATION_NEEDED_CHANGE_SUMMARY = 'notifiable-alteration-needed/change-summary',
NOTIFIABLE_ALTERATION_NEEDED_TYRE_SEARCH = 'notifiable-alteration-needed/tyre-search/:axleNumber',
+ NOTIFIABLE_ALTERNATION_NEEDED_EDIT_ADDITIONAL_EXAMINER_NOTE = 'notifiable-alteration-needed/edit-additional-examiner-note/:examinerNoteIndex',
TEST_RECORDS = 'test-records/test-result/:testResultId/:testNumber',
CREATE_TEST = 'test-records/create-test',
ADR_CERTIFICATE = 'adr-certificate',
diff --git a/src/app/shared/components/collapsible-text/collapsible-text.component.html b/src/app/shared/components/collapsible-text/collapsible-text.component.html
new file mode 100644
index 0000000000..f5371e2a2c
--- /dev/null
+++ b/src/app/shared/components/collapsible-text/collapsible-text.component.html
@@ -0,0 +1,26 @@
+
+ {{ text | slice: 0 : maxChars }} maxChars">...
+
+
+
+
+
+ {{ text }}
+
+
+
+
diff --git a/src/app/forms/custom-sections/adr-guidance-notes/adr-guidance-notes.component.scss b/src/app/shared/components/collapsible-text/collapsible-text.component.scss
similarity index 100%
rename from src/app/forms/custom-sections/adr-guidance-notes/adr-guidance-notes.component.scss
rename to src/app/shared/components/collapsible-text/collapsible-text.component.scss
diff --git a/src/app/shared/components/collapsible-text/collapsible-text.component.spec.ts b/src/app/shared/components/collapsible-text/collapsible-text.component.spec.ts
new file mode 100644
index 0000000000..1dd3750ca8
--- /dev/null
+++ b/src/app/shared/components/collapsible-text/collapsible-text.component.spec.ts
@@ -0,0 +1,36 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { CollapsibleTextComponent } from './collapsible-text.component';
+
+describe('CollapsibleTextComponent', () => {
+ let component: CollapsibleTextComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [CollapsibleTextComponent],
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(CollapsibleTextComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('should open when open method is called', () => {
+ component.isCollapsed = true;
+ component.open();
+ expect(component.isCollapsed).toBe(false);
+ });
+
+ it('should close when close method is called', () => {
+ component.isCollapsed = false;
+ component.close();
+ expect(component.isCollapsed).toBe(true);
+ });
+});
diff --git a/src/app/shared/components/collapsible-text/collapsible-text.component.ts b/src/app/shared/components/collapsible-text/collapsible-text.component.ts
new file mode 100644
index 0000000000..e39ff48585
--- /dev/null
+++ b/src/app/shared/components/collapsible-text/collapsible-text.component.ts
@@ -0,0 +1,21 @@
+import { Component, Input } from '@angular/core';
+
+@Component({
+ selector: 'collapsible-text',
+ templateUrl: './collapsible-text.component.html',
+ styleUrls: ['./collapsible-text.component.scss'],
+})
+export class CollapsibleTextComponent {
+
+ @Input() text: string = '';
+ @Input() maxChars: number = 0;
+ @Input() isCollapsed = true;
+
+ open() {
+ this.isCollapsed = false;
+ }
+
+ close() {
+ this.isCollapsed = true;
+ }
+}
diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts
index 94f3bf1dd5..c812d56293 100644
--- a/src/app/shared/shared.module.ts
+++ b/src/app/shared/shared.module.ts
@@ -4,6 +4,7 @@ import { RouterModule } from '@angular/router';
import { DocumentRetrievalService } from '@api/document-retrieval';
import { RoleRequiredDirective } from '@directives/app-role-required.directive';
import { FeatureToggleDirective } from '@directives/feature-toggle.directive';
+import { CollapsibleTextComponent } from '@shared/components/collapsible-text/collapsible-text.component';
import { AccordionControlComponent } from './components/accordion-control/accordion-control.component';
import { AccordionComponent } from './components/accordion/accordion.component';
import { BannerComponent } from './components/banner/banner.component';
@@ -54,6 +55,7 @@ import { TyreAxleLoadPipe } from './pipes/tyre-axle-load/tyre-axle-load.pipe';
TyreAxleLoadPipe,
GetControlLabelPipe,
FormatVehicleTypePipe,
+ CollapsibleTextComponent,
],
imports: [CommonModule, RouterModule],
exports: [
@@ -80,6 +82,7 @@ import { TyreAxleLoadPipe } from './pipes/tyre-axle-load/tyre-axle-load.pipe';
TyreAxleLoadPipe,
GetControlLabelPipe,
FormatVehicleTypePipe,
+ CollapsibleTextComponent,
],
providers: [DocumentRetrievalService],
diff --git a/src/app/store/technical-records/actions/technical-record-service.actions.ts b/src/app/store/technical-records/actions/technical-record-service.actions.ts
index 21ce912342..3e70e03b7b 100644
--- a/src/app/store/technical-records/actions/technical-record-service.actions.ts
+++ b/src/app/store/technical-records/actions/technical-record-service.actions.ts
@@ -98,7 +98,12 @@ export const clearScrollPosition = createAction(`${prefix} clearScrollPosition`)
export const clearADRDetailsBeforeUpdate = createAction(`${prefix} clearADRDetailsBeforeUpdate`);
-export const updateADRAdditionalExaminerNotes = createAction(`${prefix} handleADRExaminerNoteChanges`, props<{ username: string }>());
+export const updateADRAdditionalExaminerNotes = createAction(`${prefix} updateADRAdditionalExaminerNotes`, props<{ username: string }>());
+
+export const updateExistingADRAdditionalExaminerNote = createAction(
+ `${prefix} updateExistingADRAdditionalExaminerNote`,
+ props<{ additionalExaminerNote: string, examinerNoteIndex: number }>(),
+);
export const generateADRCertificate = createAction(`${prefix} generateADRCertificate`, props<{
systemNumber: string, createdTimestamp: string, certificateType: string
diff --git a/src/app/store/technical-records/reducers/technical-record-service.reducer.spec.ts b/src/app/store/technical-records/reducers/technical-record-service.reducer.spec.ts
index 71076d2966..9bb48cd36b 100644
--- a/src/app/store/technical-records/reducers/technical-record-service.reducer.spec.ts
+++ b/src/app/store/technical-records/reducers/technical-record-service.reducer.spec.ts
@@ -33,7 +33,7 @@ import {
updateBody,
updateBrakeForces,
updateEditingTechRecord,
- updateEditingTechRecordCancel,
+ updateEditingTechRecordCancel, updateExistingADRAdditionalExaminerNote,
updateScrollPosition,
updateTechRecord,
updateTechRecordFailure,
@@ -559,10 +559,19 @@ describe('Vehicle Technical Record Reducer', () => {
});
describe('handleADRExaminerNoteChanges', () => {
- it('should', () => {
+ beforeEach(() => {
+ const mockedDate = new Date(2024, 5, 20);
+ jest.useFakeTimers();
+ jest.setSystemTime(mockedDate);
+ });
+
+ afterEach(() => {
+ jest.useRealTimers();
+ });
+ it('should handle any changes made to the adr examiner notes', () => {
const testNote = {
note: 'testNote',
- createdAtDate: new Date().toISOString().split('T')[0],
+ createdAtDate: new Date().toISOString(),
lastUpdatedBy: 'someone',
};
const state: TechnicalRecordServiceState = {
@@ -586,4 +595,29 @@ describe('Vehicle Technical Record Reducer', () => {
.toContainEqual(testNote);
});
});
+ describe('handleUpdateExistingADRExaminerNote', () => {
+ it('should', () => {
+ const state: TechnicalRecordServiceState = {
+ ...initialState,
+ editingTechRecord: {
+ systemNumber: 'foo',
+ createdTimestamp: 'bar',
+ vin: 'testVin',
+ techRecord_adrDetails_additionalExaminerNotes: [
+ {
+ note: 'foo',
+ createdAtDate: 'bar',
+ lastUpdatedBy: 'foo',
+ },
+ ],
+ } as unknown as TechRecordType<'put'>,
+ loading: true,
+ };
+ const newNote = 'foobar';
+ const action = updateExistingADRAdditionalExaminerNote({ additionalExaminerNote: newNote, examinerNoteIndex: 0 });
+ const newState = vehicleTechRecordReducer(state, action);
+ const editingTechRecord = newState.editingTechRecord as unknown as (NonVerbTechRecordType<'hgv' | 'lgv' | 'trl'>);
+ expect(editingTechRecord.techRecord_adrDetails_additionalExaminerNotes![0].note).toEqual(newNote);
+ });
+ });
});
diff --git a/src/app/store/technical-records/reducers/technical-record-service.reducer.ts b/src/app/store/technical-records/reducers/technical-record-service.reducer.ts
index 4639b484da..320ce60951 100644
--- a/src/app/store/technical-records/reducers/technical-record-service.reducer.ts
+++ b/src/app/store/technical-records/reducers/technical-record-service.reducer.ts
@@ -61,7 +61,7 @@ import {
updateBody,
updateBrakeForces,
updateEditingTechRecord,
- updateEditingTechRecordCancel,
+ updateEditingTechRecordCancel, updateExistingADRAdditionalExaminerNote,
updateScrollPosition,
updateTechRecord,
updateTechRecordFailure,
@@ -153,6 +153,8 @@ export const vehicleTechRecordReducer = createReducer(
on(updateADRAdditionalExaminerNotes, (state, action) => handleADRExaminerNoteChanges(state, action.username)),
+ on(updateExistingADRAdditionalExaminerNote, (state, action) => handleUpdateExistingADRExaminerNote(state, action)),
+
on(addAxle, (state) => handleAddAxle(state)),
on(removeAxle, (state, action) => handleRemoveAxle(state, action)),
@@ -425,6 +427,7 @@ function handleClearADRDetails(state: TechnicalRecordServiceState) {
editingTechRecord: {
...editingTechRecord,
techRecord_adrDetails_vehicleDetails_type: null,
+ techRecord_adrDetails_vehicleDetails_usedOnInternationalJourneys: null,
techRecord_adrDetails_vehicleDetails_approvalDate: null,
techRecord_adrDetails_permittedDangerousGoods: null,
...nulledCompatibilityGroupJ,
@@ -448,6 +451,7 @@ function handleClearADRDetails(state: TechnicalRecordServiceState) {
techRecord_adrDetails_additionalNotes_number: null,
techRecord_adrDetails_adrTypeApprovalNo: null,
techRecord_adrDetails_adrCertificateNotes: null,
+ techRecord_adrDetails_newCertificateRequested: null,
...nulledTankDetails,
},
};
@@ -536,7 +540,7 @@ function handleADRExaminerNoteChanges(state: TechnicalRecordServiceState, userna
const additionalExaminerNotes = {
note: additionalNoteTechRecord.techRecord_adrDetails_additionalExaminerNotes_note,
lastUpdatedBy: username,
- createdAtDate: new Date().toISOString().split('T')[0],
+ createdAtDate: new Date().toISOString(),
};
if (additionalNoteTechRecord.techRecord_adrDetails_additionalExaminerNotes === null
|| additionalNoteTechRecord.techRecord_adrDetails_additionalExaminerNotes === undefined) {
@@ -547,3 +551,17 @@ function handleADRExaminerNoteChanges(state: TechnicalRecordServiceState, userna
}
return { ...state, editingTechRecord: additionalNoteTechRecord as unknown as (TechRecordType<'put'>) };
}
+
+function handleUpdateExistingADRExaminerNote(
+ state: TechnicalRecordServiceState,
+ action: { additionalExaminerNote: string, examinerNoteIndex: number },
+) {
+ const { editingTechRecord } = state;
+ const editedTechRecord = editingTechRecord as unknown as
+ (NonVerbTechRecordType<'hgv' | 'lgv' | 'trl'>);
+ if (editedTechRecord) {
+ const examinerNotes = editedTechRecord.techRecord_adrDetails_additionalExaminerNotes;
+ examinerNotes![action.examinerNoteIndex].note = action.additionalExaminerNote;
+ }
+ return { ...state, editingTechRecord: editingTechRecord as unknown as (TechRecordType<'put'>) };
+}
diff --git a/src/assets/featureToggle.int.json b/src/assets/featureToggle.int.json
index 8c5f462fcb..14c9ac4699 100644
--- a/src/assets/featureToggle.int.json
+++ b/src/assets/featureToggle.int.json
@@ -2,6 +2,5 @@
"testToggle": false,
"adrToggle": true,
"adrCertToggle": true,
- "requiredStandards": false
+ "requiredStandards": true
}
-
\ No newline at end of file
diff --git a/src/assets/featureToggle.preprod.json b/src/assets/featureToggle.preprod.json
index 8c5f462fcb..14c9ac4699 100644
--- a/src/assets/featureToggle.preprod.json
+++ b/src/assets/featureToggle.preprod.json
@@ -2,6 +2,5 @@
"testToggle": false,
"adrToggle": true,
"adrCertToggle": true,
- "requiredStandards": false
+ "requiredStandards": true
}
-
\ No newline at end of file
diff --git a/src/assets/featureToggle.prod.json b/src/assets/featureToggle.prod.json
index 9fd2e25cdb..df58ab6e4b 100644
--- a/src/assets/featureToggle.prod.json
+++ b/src/assets/featureToggle.prod.json
@@ -2,5 +2,5 @@
"testToggle": false,
"adrToggle": true,
"adrCertToggle": true,
- "requiredStandards": false
+ "requiredStandards": true
}
|