From bcb751aa6b5f350f2782649b3296d6000069061d Mon Sep 17 00:00:00 2001
From: Rhys Lewis <88314925+RLCorp@users.noreply.github.com>
Date: Tue, 6 Aug 2024 16:00:38 +0100
Subject: [PATCH] mes-examinerRecordsLocationListBugFix (#1677)
* Fixed a problem where the limited details functionality on the location list would not work properly
* removed unnecessary brackets
* Changed typing on variables
* Moved category and location logic to a new function and included unit tests
* Replaced instances of omitting a value from an interface with a new interface that extends from the previosu
* optimised loops
* Removed remaining omit
* removed unneeded comment
---
.../__tests__/examiner-records.page.spec.ts | 209 ++++++++++++++---
.../examiner-reports-card.ts | 13 +-
.../examiner-records/examiner-records.page.ts | 221 ++++++++++--------
.../examiner-records.selector.ts | 32 +--
.../examiner-records/examiner-records.ts | 4 +-
src/components/common/chart/chart.ts | 4 +-
6 files changed, 337 insertions(+), 146 deletions(-)
diff --git a/src/app/pages/examiner-records/__tests__/examiner-records.page.spec.ts b/src/app/pages/examiner-records/__tests__/examiner-records.page.spec.ts
index 5cb2be72b..88a5fcbcb 100644
--- a/src/app/pages/examiner-records/__tests__/examiner-records.page.spec.ts
+++ b/src/app/pages/examiner-records/__tests__/examiner-records.page.spec.ts
@@ -25,7 +25,10 @@ import { DASHBOARD_PAGE } from '@pages/page-names.constants';
import { ScreenOrientation } from '@capawesome/capacitor-screen-orientation';
import { ScrollDetail } from '@ionic/core';
import moment from 'moment';
-import { selectCachedExaminerRecords, selectLastCachedDate } from '@store/examiner-records/examiner-records.selectors';
+import {
+ selectCachedExaminerRecords,
+ selectLastCachedDate,
+} from '@store/examiner-records/examiner-records.selectors';
import { ExaminerRecordModel } from '@dvsa/mes-microservice-common/domain/examiner-records';
import { DateRange } from '@shared/helpers/date-time';
@@ -47,8 +50,8 @@ describe('ExaminerRecordsPage', () => {
circuits$: of([]),
locationList$: of([]),
categoryList$: of([]),
- emergencyStops$: of([])
- }
+ emergencyStops$: of([]),
+ };
const mockTests: ExaminerRecordModel[] = [
{
testCategory: TestCategory.B,
@@ -240,11 +243,51 @@ describe('ExaminerRecordsPage', () => {
});
describe('ngOnInit', () => {
- it('should call setFilterLists', () => {
+ it('should initialize testResults and update testSubject$', async () => {
+ spyOn(component, 'removeDuplicatesAndSort').and.returnValue(mockTests);
+ spyOn(component.testSubject$, 'next');
+
+ await component.ngOnInit();
+
+ expect(component.removeDuplicatesAndSort).toHaveBeenCalledWith(component.getLocalResults());
+ expect(component.testSubject$.next).toHaveBeenCalledWith(mockTests);
+ });
+
+ it('should set default date filter', async () => {
+ spyOn(component, 'handleDateFilter');
+
+ await component.ngOnInit();
+
+ expect(component.handleDateFilter).toHaveBeenCalledWith(
+ {
+ detail: { value: component.defaultDate },
+ } as CustomEvent);
+ });
+
+ it('should set categorySelectPristine to false if categorySubject$ has value', async () => {
+ component.categorySubject$.next(TestCategory.B);
+
+ await component.ngOnInit();
+
+ expect(component.categorySelectPristine).toBeFalse();
+ });
+
+ it('should set locationSelectPristine to false if locationSubject$ has value', async () => {
+ component.locationSubject$.next(1);
+
+ await component.ngOnInit();
+
+ expect(component.locationSelectPristine).toBeFalse();
+ });
+
+ it('should set location filter and fetch online records', async () => {
spyOn(component, 'setLocationFilter');
+ spyOn(component, 'getOnlineRecords');
+
+ await component.ngOnInit();
- component.ngOnInit();
expect(component.setLocationFilter).toHaveBeenCalled();
+ expect(component.getOnlineRecords).toHaveBeenCalled();
});
});
@@ -262,6 +305,116 @@ describe('ExaminerRecordsPage', () => {
});
});
+ describe('setupCategorySelectList', () => {
+ it('should add every completed category to categoryFilterOptions', () => {
+ const categories = [
+ { item: TestCategory.B, count: 1 },
+ { item: TestCategory.C, count: 2 },
+ ];
+ component.setupCategorySelectList(categories);
+ expect(component.categoryFilterOptions).toEqual([TestCategory.B, TestCategory.C]);
+ });
+
+ it('should set the most common category as the default if current category is not included', () => {
+ const categories = [
+ { item: TestCategory.B, count: 1 },
+ { item: TestCategory.C, count: 2 },
+ ];
+ spyOn(component, 'setDefault').and.returnValue(categories[1]);
+ spyOn(component, 'handleCategoryFilter');
+
+ component.categorySubject$.next(TestCategory.A);
+ component.setupCategorySelectList(categories);
+ expect(component.categoryPlaceholder).toEqual(TestCategory.C);
+ expect(component.handleCategoryFilter).toHaveBeenCalledWith(TestCategory.C);
+ });
+
+ it('should call changeEligibleTests if current category is included in categoryFilterOptions', () => {
+ const categories = [
+ { item: TestCategory.B, count: 1 },
+ { item: TestCategory.C, count: 2 },
+ ];
+ component.categorySubject$.next(TestCategory.B);
+ spyOn(component, 'changeEligibleTests');
+ component.setupCategorySelectList(categories);
+ expect(component.changeEligibleTests).toHaveBeenCalled();
+ });
+
+ it('should set categorySelectPristine to true if most common category is set as default', () => {
+ const categories = [
+ { item: TestCategory.B, count: 1 },
+ { item: TestCategory.C, count: 2 },
+ ];
+ spyOn(component, 'setDefault').and.returnValue(categories[1]);
+ component.categorySubject$.next(TestCategory.A);
+ component.setupCategorySelectList(categories);
+ expect(component.categorySelectPristine).toBeTrue();
+ });
+
+ it('should not set categorySelectPristine if current category is included in categoryFilterOptions', () => {
+ component.categorySelectPristine = false;
+ const categories = [
+ { item: TestCategory.B, count: 1 },
+ { item: TestCategory.C, count: 2 },
+ ];
+ component.categorySubject$.next(TestCategory.B);
+ spyOn(component, 'changeEligibleTests');
+ component.setupCategorySelectList(categories);
+ expect(component.categorySelectPristine).toEqual(false);
+ });
+ });
+
+ describe('setupLocationSelectList', () => {
+ it('should add every visited location to locationFilterOptions', () => {
+ const locations = [
+ { item: { centreName: 'Centre 1', centreId: 1, costCode: 'X1' }, count: 1 },
+ { item: { centreName: 'Centre 2', centreId: 2, costCode: 'X2' }, count: 2 },
+ ];
+ component.setupLocationSelectList(locations);
+ expect(component.locationFilterOptions).toEqual([
+ { centreName: 'Centre 1', centreId: 1, costCode: 'X1' },
+ { centreName: 'Centre 2', centreId: 2, costCode: 'X2' },
+ ]);
+ });
+
+ it('should display cost code or centre id if centre name is not available', () => {
+ const locations = [
+ { item: { centreName: null, centreId: 1, costCode: 'X1' }, count: 1 },
+ { item: { centreName: null, centreId: 2, costCode: null }, count: 2 },
+ ];
+ component.setupLocationSelectList(locations);
+ expect(component.locationFilterOptions).toEqual([
+ { centreName: 'Limited details - X1', centreId: 1, costCode: 'X1' },
+ { centreName: 'Limited details - 2', centreId: 2, costCode: null },
+ ]);
+ });
+
+ it('should set the most common location as the default if current location is not included', () => {
+ const locations = [
+ { item: { centreName: 'Centre 1', centreId: 1, costCode: 'X1' }, count: 1 },
+ { item: { centreName: 'Centre 2', centreId: 2, costCode: 'X2' }, count: 2 },
+ ];
+ spyOn(component, 'setDefault').and.returnValue(locations[1]);
+ spyOn(component, 'handleLocationFilter');
+
+ component.locationSubject$.next(3);
+ component.setupLocationSelectList(locations);
+ expect(component.locationPlaceholder).toEqual('Centre 2');
+ expect(component.handleLocationFilter).toHaveBeenCalledWith(locations[1].item);
+ });
+
+ it('should set locationPlaceholder to an empty string if locations array is empty', () => {
+ const locations = [];
+ spyOn(component, 'handleLocationFilter');
+
+ component.setupLocationSelectList(locations);
+ expect(component.locationPlaceholder).toEqual('');
+ expect(component.handleLocationFilter).toHaveBeenCalledWith(
+ { centreId: null, centreName: '', costCode: '' }
+ );
+ });
+ });
+
describe('setLocationFilter', () => {
it('should set locationFilterOptions to the item property of each object in locationList$', () => {
spyOn(component, 'ngOnInit');
@@ -278,7 +431,7 @@ describe('ExaminerRecordsPage', () => {
]);
});
it('should set locationPlaceholder to the centreName property ' +
- 'of the object in the location array with the highest count', () => {
+ 'of the object in the location array with the highest count', () => {
spyOn(component, 'ngOnInit');
component.locationFilterOptions = null;
component.pageState.locationList$ = of([
@@ -289,7 +442,7 @@ describe('ExaminerRecordsPage', () => {
expect(component.locationPlaceholder).toEqual('2');
});
it('should call handleLocationFilter with the item of ' +
- 'the object in the location array with the highest count', () => {
+ 'the object in the location array with the highest count', () => {
spyOn(component, 'ngOnInit');
spyOn(component, 'handleLocationFilter');
@@ -300,7 +453,9 @@ describe('ExaminerRecordsPage', () => {
{ item: { centreName: '2', centreId: 2, costCode: 'X2' }, count: 2 },
]);
component.setLocationFilter();
- expect(component.handleLocationFilter).toHaveBeenCalledWith({ centreName: '2', centreId: 2, costCode: 'X2' });
+ expect(component.handleLocationFilter).toHaveBeenCalledWith(
+ { centreName: '2', centreId: 2, costCode: 'X2' }
+ );
});
});
@@ -320,9 +475,9 @@ describe('ExaminerRecordsPage', () => {
{
detail: {
value:
- {
- display: '1',
- },
+ {
+ display: '1',
+ },
},
} as CustomEvent,
);
@@ -333,10 +488,10 @@ describe('ExaminerRecordsPage', () => {
{
detail: {
value:
- {
- display: '1',
- val: 'today',
- },
+ {
+ display: '1',
+ val: 'today',
+ },
},
} as CustomEvent,
);
@@ -350,9 +505,9 @@ describe('ExaminerRecordsPage', () => {
{
detail: {
value:
- {
- val: '1',
- },
+ {
+ val: '1',
+ },
},
} as CustomEvent,
);
@@ -385,8 +540,8 @@ describe('ExaminerRecordsPage', () => {
});
describe('getOnlineRecords', () => {
- // eslint-disable-next-line max-len
- it('should dispatch LoadingExaminerRecords and GetExaminerRecords actions when cached records are not available or last cached date is different', () => {
+ it('should dispatch LoadingExaminerRecords and GetExaminerRecords actions when ' +
+ 'cached records are not available or last cached date is different', () => {
store$.overrideSelector(selectCachedExaminerRecords, null);
store$.overrideSelector(selectLastCachedDate, 'some other date');
@@ -580,8 +735,8 @@ describe('ExaminerRecordsPage', () => {
describe('cardClicked', () => {
it('should dispatch the store', () => {
- component.cardClicked({isExpanded: false, title: 'test'});
- expect(component.store$.dispatch).toHaveBeenCalledWith(ClickDataCard({isExpanded: false, title: 'test'}));
+ component.cardClicked({ isExpanded: false, title: 'test' });
+ expect(component.store$.dispatch).toHaveBeenCalledWith(ClickDataCard({ isExpanded: false, title: 'test' }));
});
});
@@ -599,7 +754,7 @@ describe('ExaminerRecordsPage', () => {
emergencyStops: [],
circuits: [],
locationList: [],
- categoryList: []
+ categoryList: [],
};
expect(component.displayNoDataCard(emptyData)).toBeTrue();
});
@@ -616,8 +771,8 @@ describe('ExaminerRecordsPage', () => {
testCount: 1,
emergencyStops: [],
circuits: [],
- locationList: [{item: {centreName: 'Test Centre 1', centreId: 1, costCode: 'TC1'}, count: 1}],
- categoryList: [{item: TestCategory.B, count: 1}]
+ locationList: [{ item: { centreName: 'Test Centre 1', centreId: 1, costCode: 'TC1' }, count: 1 }],
+ categoryList: [{ item: TestCategory.B, count: 1 }],
};
spyOn(component, 'getTotal').and.returnValue(1);
@@ -637,7 +792,7 @@ describe('ExaminerRecordsPage', () => {
emergencyStops: [],
circuits: [],
locationList: [],
- categoryList: []
+ categoryList: [],
};
expect(component.displayNoDataCard(data)).toBeTrue();
});
@@ -670,7 +825,7 @@ describe('ExaminerRecordsPage', () => {
const expectedText = 'Displaying 2 Category C' +
' tests, from 01/02/2021 to 28/02/2021' +
- '
at Test Centre 2'
+ '
at Test Centre 2';
expect(component.getLabelText()).toEqual(expectedText);
});
diff --git a/src/app/pages/examiner-records/components/examiner-reports-card/examiner-reports-card.ts b/src/app/pages/examiner-records/components/examiner-reports-card/examiner-reports-card.ts
index 8206d9222..e79754b5e 100644
--- a/src/app/pages/examiner-records/components/examiner-reports-card/examiner-reports-card.ts
+++ b/src/app/pages/examiner-records/components/examiner-reports-card/examiner-reports-card.ts
@@ -1,6 +1,6 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { AccessibilityService } from '@providers/accessibility/accessibility.service';
-import { ExaminerRecordData } from '@pages/examiner-records/examiner-records.selector';
+import { ExaminerRecordDataWithPercentage } from '@pages/examiner-records/examiner-records.selector';
import { ChartType } from 'ng-apexcharts';
export interface ExaminerReportsCardClick {
@@ -20,7 +20,7 @@ export class ExaminerReportsCard {
onCardClick: EventEmitter = new EventEmitter();
@Input()
- passedData: ExaminerRecordData[] = null;
+ passedData: ExaminerRecordDataWithPercentage[] = null;
@Input()
chartID: string = null;
@Input()
@@ -87,11 +87,11 @@ export class ExaminerReportsCard {
* The `count` property is converted to a number before summing.
*
* @template T - The type of the data contained in the `ExaminerRecordData` objects.
- * @param {ExaminerRecordData[]} value - The array of `ExaminerRecordData` objects to be totaled.
+ * @param {ExaminerRecordDataWithPercentage[]} value - The array of `ExaminerRecordData` objects to be totaled.
* @returns {number} The total count of all `ExaminerRecordData` objects.
*/
getTotal = (
- value: ExaminerRecordData[],
+ value: ExaminerRecordDataWithPercentage[],
): number => value.reduce((total, val) => total + Number(val.count), 0);
/**
@@ -102,11 +102,12 @@ export class ExaminerReportsCard {
* containing an empty array.
*
* @template T - The type of the data contained in the `ExaminerRecordData` objects.
- * @param {ExaminerRecordData[]} examinerRecordData - The array of `ExaminerRecordData` objects to be formatted.
+ * @param {ExaminerRecordDataWithPercentage[]} examinerRecordData - The array of `ExaminerRecordData` objects
+ * to be formatted.
* @returns {T[][]} A two-dimensional array where each inner array contains the values of an `ExaminerRecordData`
* object.
*/
- filterDataForGrid(examinerRecordData: ExaminerRecordData[]): T[][] {
+ filterDataForGrid(examinerRecordData: ExaminerRecordDataWithPercentage[]): T[][] {
if (!!examinerRecordData && examinerRecordData.length > 0) {
return examinerRecordData.map((obj) => Object.values(obj) as T[]);
}
diff --git a/src/app/pages/examiner-records/examiner-records.page.ts b/src/app/pages/examiner-records/examiner-records.page.ts
index 69a0dbf66..0f0803a4c 100644
--- a/src/app/pages/examiner-records/examiner-records.page.ts
+++ b/src/app/pages/examiner-records/examiner-records.page.ts
@@ -17,6 +17,7 @@ import {
} from '@pages/examiner-records/examiner-records.actions';
import {
ExaminerRecordData,
+ ExaminerRecordDataWithPercentage,
getBalanceQuestions,
getCategories,
getCircuits,
@@ -64,35 +65,35 @@ import {
import { selectEmployeeId } from '@store/app-info/app-info.selectors';
export interface ExaminerRecordsPageStateData {
- routeGrid: ExaminerRecordData[],
- manoeuvresGrid: ExaminerRecordData[],
- showMeQuestionsGrid: ExaminerRecordData[],
- independentDrivingGrid: ExaminerRecordData[],
- tellMeQuestionsGrid: ExaminerRecordData[],
- safetyGrid: ExaminerRecordData[],
- balanceGrid: ExaminerRecordData[],
+ routeGrid: ExaminerRecordDataWithPercentage[],
+ manoeuvresGrid: ExaminerRecordDataWithPercentage[],
+ showMeQuestionsGrid: ExaminerRecordDataWithPercentage[],
+ independentDrivingGrid: ExaminerRecordDataWithPercentage[],
+ tellMeQuestionsGrid: ExaminerRecordDataWithPercentage[],
+ safetyGrid: ExaminerRecordDataWithPercentage[],
+ balanceGrid: ExaminerRecordDataWithPercentage[],
testCount: number,
- emergencyStops: ExaminerRecordData[],
- circuits: ExaminerRecordData[],
- locationList: { item: TestCentre, count: number }[],
- categoryList: { item: TestCategory, count: number }[]
+ emergencyStops: ExaminerRecordDataWithPercentage[],
+ circuits: ExaminerRecordDataWithPercentage[],
+ locationList: ExaminerRecordData[],
+ categoryList: ExaminerRecordData[]
}
interface ExaminerRecordsState {
cachedRecords$: Observable;
isLoadingRecords$: Observable;
- routeNumbers$: Observable[]>;
- manoeuvres$: Observable[]>;
- showMeQuestions$: Observable[]>;
- tellMeQuestions$: Observable[]>;
- safetyQuestions$: Observable[]>;
- balanceQuestions$: Observable[]>;
- independentDriving$: Observable[]>;
+ routeNumbers$: Observable[]>;
+ manoeuvres$: Observable[]>;
+ showMeQuestions$: Observable[]>;
+ tellMeQuestions$: Observable[]>;
+ safetyQuestions$: Observable[]>;
+ balanceQuestions$: Observable[]>;
+ independentDriving$: Observable[]>;
testCount$: Observable;
- locationList$: Observable<{ item: TestCentre, count: number }[]>;
- categoryList$: Observable<{ item: TestCategory, count: number }[]>;
- emergencyStops$: Observable[]>;
- circuits$: Observable[]>;
+ locationList$: Observable[]>;
+ categoryList$: Observable[]>;
+ emergencyStops$: Observable[]>;
+ circuits$: Observable[]>;
}
@Component({
@@ -104,14 +105,14 @@ export class ExaminerRecordsPage implements OnInit {
merged$: Observable;
form: UntypedFormGroup = new UntypedFormGroup({});
- testSubject$ = new BehaviorSubject(null);
- testsInRangeSubject$ = new BehaviorSubject(null);
- eligTestSubject$ = new BehaviorSubject(null);
- rangeSubject$ = new BehaviorSubject(null);
- locationSubject$ = new BehaviorSubject(null);
- categorySubject$ = new BehaviorSubject(null);
+ testSubject$: BehaviorSubject = new BehaviorSubject(null);
+ testsInRangeSubject$: BehaviorSubject = new BehaviorSubject(null);
+ eligTestSubject$: BehaviorSubject = new BehaviorSubject(null);
+ rangeSubject$: BehaviorSubject = new BehaviorSubject(null);
+ locationSubject$: BehaviorSubject = new BehaviorSubject(null);
+ categorySubject$: BehaviorSubject = new BehaviorSubject(null);
pageState: ExaminerRecordsState;
- hideMainContent = false;
+ hideMainContent: boolean = false;
colourOption: ColourScheme = this.getColour(this.store$.selectSignal(selectColourScheme)());
categoryPlaceholder: string;
locationPlaceholder: string;
@@ -295,7 +296,7 @@ export class ExaminerRecordsPage implements OnInit {
* @returns {Observable} An observable that emits the result of applying the function `fn` to the eligible tests,
* date range, category, and location.
*/
- private getLocationsByParameters = (fn: (
+ getLocationsByParameters = (fn: (
tests: ExaminerRecordModel[],
range: DateRange,
category: string,
@@ -327,7 +328,10 @@ export class ExaminerRecordsPage implements OnInit {
* @param {ExaminerRecordModel[]} cachedExaminerRecords - The cached online records to be merged.
* @returns {ExaminerRecordModel[]} The merged array of local and cached online records.
*/
- mergeWithOnlineResults(localRecords: ExaminerRecordModel[], cachedExaminerRecords: ExaminerRecordModel[]) {
+ mergeWithOnlineResults(
+ localRecords: ExaminerRecordModel[],
+ cachedExaminerRecords: ExaminerRecordModel[]
+ ): ExaminerRecordModel[] {
this.cachedExaminerRecords = cachedExaminerRecords;
if (!this.cachedExaminerRecords) {
this.store$.dispatch(DisplayPartialBanner())
@@ -340,7 +344,7 @@ export class ExaminerRecordsPage implements OnInit {
}
/**
- * Removes duplicate entries from an array and sorts its content by the most recent date.
+ * Removes duplicate entries from an array and sorts the content by the most recent date.
*
* This method filters out duplicate entries in the provided array based on the `appRef` property
* and then sorts the remaining entries in descending order by the `startDate` property.
@@ -398,6 +402,85 @@ export class ExaminerRecordsPage implements OnInit {
return result;
}
+ /**
+ * Sets up the category select list with the provided values.
+ *
+ * This method performs the following actions:
+ * 1. Initializes the `categoryFilterOptions` array.
+ * 2. Adds every completed category to the `categoryFilterOptions` array.
+ * 3. Checks if the current category is included in the `categoryFilterOptions`.
+ * - If not, finds the most common category and sets it as the default.
+ * - If yes, calls `changeEligibleTests` to update the eligible tests.
+ *
+ * @param {ExaminerRecordData[]} categories - The array of category data to
+ * set up the select list.
+ */
+ setupCategorySelectList(categories: ExaminerRecordData[]) {
+ this.categoryFilterOptions = [];
+
+ //add every completed category to category array
+ this.categoryFilterOptions = categories.map((val) => val.item);
+
+ if (!this.categoryFilterOptions.includes(this.categorySubject$.value)) {
+ //find most common category and set it as the default
+ const mostUsed: ExaminerRecordData = this.setDefault(categories);
+
+ if (!!mostUsed) {
+ this.categoryPlaceholder = mostUsed.item;
+ this.handleCategoryFilter(mostUsed.item);
+ this.categorySelectPristine = true;
+ }
+ } else {
+ this.changeEligibleTests();
+ }
+ }
+
+ /**
+ * Sets up the location select list with the provided values.
+ *
+ * This method performs the following actions:
+ * 1. Initializes the `locationFilterOptions` array.
+ * 2. Adds every visited location to the `locationFilterOptions` array.
+ * 3. Checks if the current location is included in the `locationFilterOptions`.
+ * - If not, finds the most common location and sets it as the default.
+ *
+ * @param {ExaminerRecordData[]} locations - The array of location data to
+ * set up the select list.
+ */
+ setupLocationSelectList(locations: ExaminerRecordData[]) {
+ this.locationFilterOptions = [];
+
+ //add every visited location to location array
+ locations.forEach((val: ExaminerRecordData) => {
+ if (!val.item?.centreName) {
+ // Should there be no centre name available, display cost code or centre id,
+ // depending on whether cost code is available
+ val.item = {
+ ...val.item,
+ centreName: `Limited details - ${
+ !!val.item.costCode ? val.item.costCode : val.item.centreId.toString()
+ }`
+ }
+ }
+ this.locationFilterOptions.push(val.item);
+ });
+
+ if (!this.locationFilterOptions.map(({ centreId }) => centreId)
+ .includes(this.locationSubject$.value)) {
+ //find most common location and set it as the default
+ const mostUsed: ExaminerRecordData = this.setDefault(locations);
+ if (!!mostUsed) {
+ this.locationPlaceholder = mostUsed.item.centreName;
+ this.handleLocationFilter(mostUsed.item);
+ this.locationSelectPristine = true;
+ } else if (locations.length === 0) {
+ this.locationPlaceholder = '';
+ this.handleLocationFilter({ centreId: null, centreName: '', costCode: '' });
+ this.locationSelectPristine = true;
+ }
+ }
+ }
+
/**
* Initializes the component and sets up the necessary data and subscriptions.
*
@@ -438,61 +521,14 @@ export class ExaminerRecordsPage implements OnInit {
circuits$: this.getTestsByParameters(getCircuits),
locationList$: this.getLocationsByParameters(getLocations)
.pipe(
- tap((value) => {
- this.locationFilterOptions = [];
-
- //add every visited location to location array
- value.forEach((val) => {
- if (!(val.item.centreName)) {
- // Should there be no centre name available, display cost code or centre id,
- // depending on whether cost code is available
- val.item.centreName = `Limited details - ${
- !!val.item.costCode ? val.item.costCode : val.item.centreId.toString()
- }`
- }
- this.locationFilterOptions.push(val.item);
- });
-
-
- if (!this.locationFilterOptions.map(({ centreId }) => centreId)
- .includes(this.locationSubject$.value)) {
- //find most common location and set it as the default
- const mostUsed = this.setDefault(value);
- if (!!mostUsed) {
- this.locationPlaceholder = mostUsed.item.centreName;
- this.handleLocationFilter(mostUsed.item);
- this.locationSelectPristine = true;
- } else if (value.length === 0) {
- this.locationPlaceholder = '';
- this.handleLocationFilter({ centreId: null, centreName: '', costCode: '' });
- this.locationSelectPristine = true;
- }
- }
-
+ tap((value: ExaminerRecordData[]) => {
+ this.setupLocationSelectList(value);
}),
),
categoryList$: this.getCategoriesByParameters(getCategories)
.pipe(
- tap((value: Omit, 'percentage'>[]) => {
- this.categoryFilterOptions = [];
-
- //add every completed category to category array
- value.forEach((val) => {
- this.categoryFilterOptions.push(val.item);
- });
-
- if (!this.categoryFilterOptions.includes(this.categorySubject$.value)) {
- //find most common category and set it as the default
- const mostUsed = this.setDefault(value);
-
- if (!!mostUsed) {
- this.categoryPlaceholder = mostUsed.item;
- this.handleCategoryFilter(mostUsed.item);
- this.categorySelectPristine = true;
- }
- } else {
- this.changeEligibleTests();
- }
+ tap((value: ExaminerRecordData[]) => {
+ this.setupCategorySelectList(value)
}),
),
emergencyStops$: this.getTestsByParameters(getStartedTestCount)
@@ -585,12 +621,11 @@ export class ExaminerRecordsPage implements OnInit {
let mostUsed = null;
this.pageState.locationList$.subscribe(value => {
- value.forEach((val) => {
- this.locationFilterOptions.push(val.item);
- });
+ //populate location filter options
+ this.locationFilterOptions = value.map((val) => val.item);
+ //find most common location and set it as the default
mostUsed = this.setDefault(value);
- })
- .unsubscribe();
+ }).unsubscribe();
if (!!mostUsed) {
this.locationPlaceholder = mostUsed.item.centreName;
@@ -607,11 +642,11 @@ export class ExaminerRecordsPage implements OnInit {
* 2. Uses the reduce function to find the element with the highest count.
*
* @template T The type of the elements in the data array.
- * @param {Omit, 'percentage'>[]} data - The array of data elements to search.
- * @returns {Omit, 'percentage'> | null} The element with the highest count, or null if the
+ * @param {ExaminerRecordData[]} data - The array of data elements to search.
+ * @returns {ExaminerRecordData} The element with the highest count, or null if the
* input data is null or empty.
*/
- setDefault(data: Omit, 'percentage'>[]): Omit, 'percentage'> {
+ setDefault(data: ExaminerRecordData[]): ExaminerRecordData {
if (!data || data?.length === 0) {
return null;
}
@@ -820,11 +855,11 @@ export class ExaminerRecordsPage implements OnInit {
* by summing up the `count` property of each object in the array.
*
* @template T The type of the data in the `ExaminerRecordData` objects.
- * @param {ExaminerRecordData[]} value - The array of `ExaminerRecordData` objects.
+ * @param {ExaminerRecordDataWithPercentage[]} value - The array of `ExaminerRecordData` objects.
* @returns {number} The total count of individual instances of data.
*/
getTotal = (
- value: ExaminerRecordData[],
+ value: ExaminerRecordDataWithPercentage[],
): number => value.reduce((total, val) => total + Number(val.count), 0);
/**
diff --git a/src/app/pages/examiner-records/examiner-records.selector.ts b/src/app/pages/examiner-records/examiner-records.selector.ts
index 21a36b6f6..74b9ed13b 100644
--- a/src/app/pages/examiner-records/examiner-records.selector.ts
+++ b/src/app/pages/examiner-records/examiner-records.selector.ts
@@ -10,12 +10,15 @@ import { manoeuvreTypeLabels as manoeuvreTypeLabelsCatBE } from '@shared/constan
import { manoeuvreTypeLabels as manoeuvreTypeLabelsCatADI2 } from '@shared/constants/competencies/catadi2-manoeuvres';
import { isAnyOf } from '@shared/helpers/simplifiers';
import { ExaminerRecordModel } from '@dvsa/mes-microservice-common/domain/examiner-records';
-import { ExaminerRecordsRange } from '@providers/examiner-records/examiner-records';
// Generic `T` is the configurable type of the item
export interface ExaminerRecordData {
item: T;
count: number;
+}
+
+// Generic `T` is the configurable type of the item
+export interface ExaminerRecordDataWithPercentage extends ExaminerRecordData{
percentage: string;
}
@@ -57,7 +60,7 @@ export const getIndex = (item: string) => {
export const getEligibleTests = (
startedTests: ExaminerRecordModel[],
category: TestCategory = null,
- range: ExaminerRecordsRange = null,
+ range: DateRange = null,
centreId: number = null,
filterByLocation: boolean = true,
filterByCategory: boolean = true,
@@ -96,14 +99,13 @@ export const getEmergencyStopCount = (
*/
export const getLocations = (
startedTests: ExaminerRecordModel[],
- range: ExaminerRecordsRange = null,
- // Omit is a TS type, to remove a property from an interface
-): Omit, 'percentage'>[] => {
+ range: DateRange = null,
+): ExaminerRecordData[] => {
if (startedTests) {
const data: ExaminerRecordModel[] = getEligibleTests(startedTests, null, range, null)
.filter((record) => !!get(record, 'testCentre', null).centreId);
- return uniqBy(data.map(({ testCentre }) => {
+ return uniqBy(data.map(({ testCentre }): ExaminerRecordData => {
return {
item: testCentre,
count: data.filter((val) => val.testCentre.centreId === testCentre.centreId).length,
@@ -122,7 +124,7 @@ export const getLocations = (
export const getIndependentDrivingStats = (
startedTests: ExaminerRecordModel[],
category: TestCategory,
-): ExaminerRecordData[] => {
+): ExaminerRecordDataWithPercentage[] => {
//IndependentDriving is not applicable to the following categories, and so we can avoid the entire function
if (!startedTests || !category || isAnyOf(category, [
TestCategory.ADI3, TestCategory.SC,
@@ -166,7 +168,7 @@ export const getIndependentDrivingStats = (
export const getCircuits = (
startedTests: ExaminerRecordModel[],
category: TestCategory,
-): ExaminerRecordData[] => {
+): ExaminerRecordDataWithPercentage[] => {
//getCircuits is only applicable to the following categories, and so we can avoid the entire function
if (!startedTests || !category || !isAnyOf(category, [
TestCategory.EUA1M1, TestCategory.EUA2M1, TestCategory.EUAM1, TestCategory.EUAMM1,
@@ -198,7 +200,7 @@ export const getCircuits = (
*/
export const getCategories = (
startedTests: ExaminerRecordModel[],
- range: ExaminerRecordsRange,
+ range: DateRange,
category: TestCategory,
centreId: number,
): {
@@ -239,7 +241,7 @@ export const getStartedTestCount = (
*/
export const getRouteNumbers = (
startedTests: ExaminerRecordModel[],
-): ExaminerRecordData[] => {
+): ExaminerRecordDataWithPercentage[] => {
if (startedTests) {
const data = (startedTests)
.filter((record: ExaminerRecordModel) => get(record, 'routeNumber', null) !== null);
@@ -265,7 +267,7 @@ export const getRouteNumbers = (
export const getSafetyQuestions = (
startedTests: ExaminerRecordModel[],
category: TestCategory = null,
-): ExaminerRecordData[] => {
+): ExaminerRecordDataWithPercentage[] => {
const qp = new QuestionProvider();
if (startedTests) {
@@ -306,7 +308,7 @@ export const getSafetyQuestions = (
export const getBalanceQuestions = (
startedTests: ExaminerRecordModel[],
category: TestCategory = null,
-): ExaminerRecordData[] => {
+): ExaminerRecordDataWithPercentage[] => {
const qp = new QuestionProvider();
if (startedTests) {
@@ -345,7 +347,7 @@ export const getBalanceQuestions = (
export const getShowMeQuestions = (
startedTests: ExaminerRecordModel[],
category: TestCategory = null,
-): ExaminerRecordData[] => {
+): ExaminerRecordDataWithPercentage[] => {
const qp = new QuestionProvider();
if (startedTests) {
@@ -377,7 +379,7 @@ export const getShowMeQuestions = (
export const getTellMeQuestions = (
startedTests: ExaminerRecordModel[],
category: TestCategory = null,
-): ExaminerRecordData[] => {
+): ExaminerRecordDataWithPercentage[] => {
const qp = new QuestionProvider();
if (startedTests) {
@@ -423,7 +425,7 @@ export const getManoeuvreTypeLabels = (category: TestCategory, type?: ManoeuvreT
export const getManoeuvresUsed = (
startedTests: ExaminerRecordModel[],
category: TestCategory = null,
-): ExaminerRecordData[] => {
+): ExaminerRecordDataWithPercentage[] => {
let faultsEncountered: string[] = [];
let manoeuvreTypeLabels: string[] = [];
if (category) {
diff --git a/src/app/providers/examiner-records/examiner-records.ts b/src/app/providers/examiner-records/examiner-records.ts
index c9872624b..11ac35bb4 100644
--- a/src/app/providers/examiner-records/examiner-records.ts
+++ b/src/app/providers/examiner-records/examiner-records.ts
@@ -28,11 +28,9 @@ export const enum ColourEnum {
GREYSCALE = 'Greyscale',
}
-export type ExaminerRecordsRange = DateRange;
-
export interface SelectableDateRange {
display: string;
- val: ExaminerRecordsRange;
+ val: DateRange;
}
export type DESChartTypes = Extract;
@Injectable()
diff --git a/src/components/common/chart/chart.ts b/src/components/common/chart/chart.ts
index 96e9101b1..e344eb837 100644
--- a/src/components/common/chart/chart.ts
+++ b/src/components/common/chart/chart.ts
@@ -2,7 +2,7 @@ import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/cor
import ApexCharts from 'apexcharts';
import { ApexAxisChartSeries, ApexNonAxisChartSeries, ApexOptions, ChartType } from 'ng-apexcharts';
import { isEqual } from 'lodash-es';
-import { ExaminerRecordData } from '@pages/examiner-records/examiner-records.selector';
+import { ExaminerRecordDataWithPercentage } from '@pages/examiner-records/examiner-records.selector';
@Component({
selector: 'chart',
@@ -19,7 +19,7 @@ export class ChartComponent implements OnInit, OnChanges {
public chartType: ChartType = 'pie';
@Input()
- public passedData: ExaminerRecordData[] = null;
+ public passedData: ExaminerRecordDataWithPercentage[] = null;
@Input()
public showLegend: boolean = false;