From 05e509d445dd891b2646c55b58406813f3a23669 Mon Sep 17 00:00:00 2001 From: kurilova Date: Fri, 30 Aug 2024 12:12:42 +0000 Subject: [PATCH] Fix UI issues --- .../device-item/device-item.component.scss | 6 +- modules/ui/src/app/mocks/device.mock.ts | 2 +- .../device-qualification-from.component.html | 36 +++++- .../device-qualification-from.component.scss | 12 +- ...evice-qualification-from.component.spec.ts | 23 +++- .../device-qualification-from.component.ts | 116 +++++++++++++----- .../app/pages/devices/devices.component.html | 4 +- .../pages/devices/devices.component.spec.ts | 5 +- .../app/pages/devices/devices.component.ts | 13 +- modules/ui/src/styles.scss | 12 +- modules/ui/src/theming/theme.scss | 3 +- modules/ui/src/theming/variables.scss | 1 + 12 files changed, 185 insertions(+), 48 deletions(-) diff --git a/modules/ui/src/app/components/device-item/device-item.component.scss b/modules/ui/src/app/components/device-item/device-item.component.scss index f5ba9ee79..fcef089dd 100644 --- a/modules/ui/src/app/components/device-item/device-item.component.scss +++ b/modules/ui/src/app/components/device-item/device-item.component.scss @@ -32,7 +32,7 @@ $border-radius: 12px; padding: 0; grid-column-gap: 8px; grid-row-gap: 4px; - font-family: 'Open Sans', sans-serif; + font-family: $font-primary; grid-template-areas: 'manufacturer manufacturer' 'name address'; @@ -59,7 +59,7 @@ $border-radius: 12px; grid-template-columns: 1fr $icon-width; grid-column-gap: 1px; padding: 0; - font-family: 'Open Sans', sans-serif; + font-family: $font-primary; grid-template-areas: 'edit start'; &.device-item-outdated { @@ -76,7 +76,7 @@ $border-radius: 12px; padding: 0 6px 0 0; grid-column-gap: 8px; grid-row-gap: 4px; - font-family: 'Open Sans', sans-serif; + font-family: $font-primary; grid-template-areas: 'manufacturer status' 'name address'; diff --git a/modules/ui/src/app/mocks/device.mock.ts b/modules/ui/src/app/mocks/device.mock.ts index 131ce27c5..86ef4ffd7 100644 --- a/modules/ui/src/app/mocks/device.mock.ts +++ b/modules/ui/src/app/mocks/device.mock.ts @@ -65,7 +65,7 @@ export const MOCK_TEST_MODULES = [ { displayName: 'Udmi', name: 'udmi', - enabled: false, + enabled: true, }, ]; diff --git a/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.html b/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.html index 188b79aaa..34ad7b9f9 100644 --- a/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.html +++ b/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.html @@ -36,7 +36,6 @@

{{ data.title }}

[selectedIndex]="selectedIndex" [title]="data.title + ' modal window'" (selectionChange)="onStepChange($event)"> - {{ selectedIndex }}
@@ -148,13 +147,18 @@

{{ data.title }}

+ + At least one test has to be selected to save a Device. +
@@ -185,10 +189,14 @@

Summary

- + The device has been configured. Please check the setup. - + No changes were made to the device configuration. Summary

+
+ Select Save to create your new device. You will then be able to + carry on your device testing journey: + +
mat-flat-button color="primary" class="save-button" - [disabled]="formPristine || !formValid" + [disabled]=" + deviceHasNoChanges(data.initialDevice, device) || !formValid + " (click)="submit()"> Save diff --git a/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.scss b/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.scss index 3cfc71cc9..5b5c9d988 100644 --- a/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.scss +++ b/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.scss @@ -273,13 +273,15 @@ $form-height: 993px; .device-qualification-form-instructions { margin-top: auto; + padding-top: 8px; color: $grey-800; text-align: center; font-family: $font-secondary; font-size: 16px; - + width: 510px; ul { text-align: left; + padding-left: 26px; } li { line-height: 24px; @@ -295,10 +297,16 @@ $form-height: 993px; padding-bottom: 14px; } -.device-qualification-form-test-modules-error ::ng-deep .device-tests-title { +.device-qualification-form-test-modules-container-error + ::ng-deep + .device-tests-title { color: $red-800; } +.device-qualification-form-test-modules-error { + padding: 0 24px; +} + @container qualification-form (height < 870px) { .device-qualification-form-page { overflow: scroll; diff --git a/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.spec.ts b/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.spec.ts index 10c9d64a8..c9f33e473 100644 --- a/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.spec.ts +++ b/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.spec.ts @@ -162,6 +162,7 @@ describe('DeviceQualificationFromComponent', () => { it('should close dialog on "cancel" click', () => { fixture.detectChanges(); + component.manufacturer.setValue('test'); ( component.deviceQualificationForm.get('steps') as FormArray ).controls.forEach(control => control.markAsDirty()); @@ -178,7 +179,7 @@ describe('DeviceQualificationFromComponent', () => { index: 0, device: { status: DeviceStatus.VALID, - manufacturer: '', + manufacturer: 'test', model: '', mac_addr: '', test_pack: 'Device qualification', @@ -186,7 +187,7 @@ describe('DeviceQualificationFromComponent', () => { technology: '', test_modules: { udmi: { - enabled: false, + enabled: true, }, connection: { enabled: true, @@ -228,12 +229,19 @@ describe('DeviceQualificationFromComponent', () => { it('should have error when no modules selected', () => { component.test_modules.setValue([false, false]); + component.test_modules.markAsTouched(); fixture.detectChanges(); - - const modules = compiled.querySelectorAll( + const modules = compiled.querySelector( + '.device-qualification-form-test-modules-container-error' + ); + const error = compiled.querySelector( '.device-qualification-form-test-modules-error' ); + expect(modules).toBeTruthy(); + expect(error?.innerHTML.trim()).toEqual( + 'At least one test has to be selected to save a Device.' + ); }); }); @@ -559,6 +567,13 @@ describe('DeviceQualificationFromComponent', () => { expect(item).toBeTruthy(); }); + it('should have instructions', () => { + const instructions = compiled.querySelector( + '.device-qualification-form-instructions' + ); + expect(instructions).toBeTruthy(); + }); + it('should save device', () => { const saveSpy = spyOn(component.devicesStore, 'saveDevice'); diff --git a/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.ts b/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.ts index 8207ce3aa..91f937fe7 100644 --- a/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.ts +++ b/modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.ts @@ -67,6 +67,7 @@ import { QualificationIconComponent } from '../../../../components/qualification import { PilotIconComponent } from '../../../../components/pilot-icon/pilot-icon.component'; import { Question } from '../../../../model/profile'; import { FormControlType } from '../../../../model/question'; +import { FocusManagerService } from '../../../../services/focus-manager.service'; const MAC_ADDRESS_PATTERN = '^[\\s]*[a-fA-F0-9]{2}(?:[:][a-fA-F0-9]{2}){5}[\\s]*$'; @@ -74,6 +75,7 @@ const MAC_ADDRESS_PATTERN = interface DialogData { title?: string; device?: Device; + initialDevice?: Device; devices: Device[]; testModules: TestModule[]; index: number; @@ -173,12 +175,8 @@ export class DeviceQualificationFromComponent ).controls.every(control => (control as FormGroup).valid); } - get deviceHasNoChanges() { - const obj1 = this.data.device; - const obj2 = this.device; - return ( - (obj1 && obj2 && this.compareObjects(obj1, obj2)) || this.formPristine - ); + deviceHasNoChanges(device1: Device | undefined, device2: Device | undefined) { + return device1 && device2 && this.compareDevices(device1, device2); } constructor( @@ -188,7 +186,8 @@ export class DeviceQualificationFromComponent public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: DialogData, public devicesStore: DevicesStore, - private element: ElementRef + private element: ElementRef, + private focusService: FocusManagerService ) { this.device = data.device; } @@ -221,7 +220,10 @@ export class DeviceQualificationFromComponent this.fillDeviceForm(this.format, this.data.device!); } if (this.data.index) { - this.selectedIndex = this.data.index; + // previous steps should be marked as interacted + for (let i = 0; i <= this.data.index; i++) { + this.goToStep(i); + } } this.dialogRef .keydownEvents() @@ -256,11 +258,11 @@ export class DeviceQualificationFromComponent } closeForm(): void { - const obj1 = this.data.device; - const obj2 = this.createDeviceFromForm(); + const device1 = this.data.initialDevice; + const device2 = this.createDeviceFromForm(); if ( - (obj1 && obj2 && this.compareObjects(obj1, obj2)) || - this.formPristine + (device1 && device2 && this.compareDevices(device1, device2)) || + (!device1 && this.deviceIsEmpty(device2)) ) { this.dialogRef.close(); } else { @@ -279,6 +281,7 @@ export class DeviceQualificationFromComponent } onStepChange(event: StepperSelectionEvent) { + this.focusService.focusFirstElementInContainer(); if (event.previouslySelectedStep.completed) { this.device = this.createDeviceFromForm(); } @@ -460,32 +463,89 @@ export class DeviceQualificationFromComponent return new FormGroup({}); } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private compareObjects(object1: any, object2: any) { - const keys1 = Object.keys(object1); - const keys2 = Object.keys(object2); - - if (keys1.length !== keys2.length) { + private compareDevices(device1: Device, device2: Device) { + if (device1.manufacturer !== device2.manufacturer) { + return false; + } + if (device1.model !== device2.model) { + return false; + } + if (device1.mac_addr !== device2.mac_addr) { + return false; + } + if (device1.type !== device2.type) { + return false; + } + if (device1.technology !== device2.technology) { + return false; + } + if (device1.test_pack !== device2.test_pack) { return false; } + const keys1 = Object.keys(device1.test_modules!); for (const key of keys1) { - const val1 = object1[key]; - const val2 = object2[key]; - const areObjects = this.isObject(val1) && this.isObject(val2); - if ( - (areObjects && !this.compareObjects(val1, val2)) || - (!areObjects && val1 !== val2) - ) { + const val1 = device1.test_modules![key]; + const val2 = device2.test_modules![key]; + if (val1.enabled !== val2.enabled) { return false; } } + if (device1.additional_info) { + for (const question of device1.additional_info) { + if ( + question.answer !== + device2.additional_info?.find( + question2 => question2.question === question.question + )?.answer + ) { + return false; + } + } + } else { + return false; + } return true; } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private isObject(object: any) { - return object != null && typeof object === 'object'; + private deviceIsEmpty(device: Device) { + if (device.manufacturer !== '') { + return false; + } + if (device.model !== '') { + return false; + } + if (device.mac_addr !== '') { + return false; + } + if (device.type !== '') { + return false; + } + if (device.technology !== '') { + return false; + } + if (device.test_pack !== TestingType.Qualification) { + return false; + } + const keys1 = Object.keys(device.test_modules!); + + for (const key of keys1) { + const val1 = device.test_modules![key]; + if (!val1.enabled) { + return false; + } + } + + if (device.additional_info) { + for (const question of device.additional_info) { + if (question.answer !== '') { + return false; + } + } + } else { + return false; + } + return true; } } diff --git a/modules/ui/src/app/pages/devices/devices.component.html b/modules/ui/src/app/pages/devices/devices.component.html index 7ea597d42..50cb640f2 100644 --- a/modules/ui/src/app/pages/devices/devices.component.html +++ b/modules/ui/src/app/pages/devices/devices.component.html @@ -22,7 +22,9 @@

Devices

{ ariaLabel: 'Create Device', data: { device: null, + initialDevice: undefined, title: 'Create Device', testModules: [], devices: [device, device, device], @@ -180,13 +181,14 @@ describe('DevicesComponent', () => { } as MatDialogRef); fixture.detectChanges(); - component.openDialog([device], MOCK_TEST_MODULES, device, true); + component.openDialog([device], MOCK_TEST_MODULES, device, device, true); expect(openSpy).toHaveBeenCalled(); expect(openSpy).toHaveBeenCalledWith(DeviceQualificationFromComponent, { ariaLabel: 'Edit device', data: { device: device, + initialDevice: device, title: 'Edit device', devices: [device], testModules: MOCK_TEST_MODULES, @@ -249,6 +251,7 @@ describe('DevicesComponent', () => { [device], MOCK_TEST_MODULES, device, + undefined, false, 0 ); diff --git a/modules/ui/src/app/pages/devices/devices.component.ts b/modules/ui/src/app/pages/devices/devices.component.ts index 61042d82e..a6fdbd74c 100644 --- a/modules/ui/src/app/pages/devices/devices.component.ts +++ b/modules/ui/src/app/pages/devices/devices.component.ts @@ -119,6 +119,7 @@ export class DevicesComponent implements OnInit, OnDestroy { openDialog( devices: Device[] = [], testModules: TestModule[], + initialDevice?: Device, selectedDevice?: Device, isEditDevice = false, index = 0 @@ -127,6 +128,7 @@ export class DevicesComponent implements OnInit, OnDestroy { ariaLabel: isEditDevice ? 'Edit device' : 'Create Device', data: { device: selectedDevice || null, + initialDevice, title: isEditDevice ? 'Edit device' : 'Create Device', testModules: testModules, devices, @@ -152,6 +154,7 @@ export class DevicesComponent implements OnInit, OnDestroy { this.openCloseDialog( devices, testModules, + initialDevice, response.device, isEditDevice, response.index @@ -173,6 +176,7 @@ export class DevicesComponent implements OnInit, OnDestroy { openCloseDialog( devices: Device[], testModules: TestModule[], + initialDevice?: Device, device?: Device, isEditDevice = false, index = 0 @@ -194,7 +198,14 @@ export class DevicesComponent implements OnInit, OnDestroy { .pipe(takeUntil(this.destroy$)) .subscribe(close => { if (!close) { - this.openDialog(devices, testModules, device, isEditDevice, index); + this.openDialog( + devices, + testModules, + initialDevice, + device, + isEditDevice, + index + ); this.devicesStore.selectDevice(null); } }); diff --git a/modules/ui/src/styles.scss b/modules/ui/src/styles.scss index 3f260e0ac..917b38763 100644 --- a/modules/ui/src/styles.scss +++ b/modules/ui/src/styles.scss @@ -17,6 +17,16 @@ @import './theming/theme'; @import 'src/theming/variables'; +@font-face { + font-family: 'Google Sans'; + font-style: normal; + src: url(https://fonts.gstatic.com/s/googlesans/v58/4Ua_rENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RFD48TE63OOYKtrwEIJllpyk.woff2) + format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, + U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, + U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + html, body { height: 100%; @@ -24,7 +34,7 @@ body { body { margin: 0; - font-family: 'Open Sans', sans-serif; + font-family: $font-primary; } /* Custom Scroollbar */ diff --git a/modules/ui/src/theming/theme.scss b/modules/ui/src/theming/theme.scss index 498db1269..09b7d9fc0 100644 --- a/modules/ui/src/theming/theme.scss +++ b/modules/ui/src/theming/theme.scss @@ -16,6 +16,7 @@ @use '@angular/material' as mat; @import url('https://fonts.googleapis.com/css2?family=Open+Sans&display=swap'); @import 'colors'; +@import 'variables'; @include mat.core(); @include mat.strong-focus-indicators( @@ -31,7 +32,7 @@ $app-secondary: mat.m2-define-palette($color-secondary); $app-blue-light-palette: mat.m2-define-palette($color-blue-light); $app-typography: mat.m2-define-typography-config( - $font-family: 'Open Sans', + $font-family: $font-primary, ); $brand-light-theme: mat.m2-define-light-theme( diff --git a/modules/ui/src/theming/variables.scss b/modules/ui/src/theming/variables.scss index dfa50880b..80021e976 100644 --- a/modules/ui/src/theming/variables.scss +++ b/modules/ui/src/theming/variables.scss @@ -17,4 +17,5 @@ $device-item-width: 352px; $profiles-drawer-width: 320px; $form-max-width: 732px; +$font-primary: 'Google Sans', sans-serif; $font-secondary: 'Roboto';