Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cypress/e2e/integration/device/device.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ describe('Test Device Page', () => {
if (Cypress.env('ISOLATE').charAt(0).toLowerCase() !== 'n') {
cy.myIntercept('GET', '**/devices?tags=Windows&$top=25&$skip=0&$count=true', {
statusCode: httpCodes.SUCCESS,
body: devices.getAll.windows.response.data
body: devices.getAll.windows.response
}).as('get-windows')

cy.goToPage('Devices')
Expand Down
16 changes: 15 additions & 1 deletion src/app/devices/devices.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ <h3 class="flex justify-center">
}
</h3>
} @else {
<mat-tab-group (selectedTabChange)="onTabChange($event.index)">
<mat-tab [label]="allTabLabel"></mat-tab>
<mat-tab [label]="activatedTabLabel"></mat-tab>
<mat-tab [label]="discoveredTabLabel"></mat-tab>
</mat-tab-group>
Comment thread
ShradhaGupta31 marked this conversation as resolved.
<div class="flex-row flex-wrap flex-1">
<div class="flex flex-66" style="width: 66%">
<mat-form-field data-cy="filterSearch" style="width: 100%">
Expand Down Expand Up @@ -120,6 +125,15 @@ <h3 class="flex justify-center">
}
</mat-cell>
</ng-container>
<!-- productType Column -->
<ng-container matColumnDef="productType">
<mat-header-cell *matHeaderCellDef mat-sort-header>{{
'devices.table.productType.value' | translate
}}</mat-header-cell>
<mat-cell *matCellDef="let element" (click)="navigateTo(element.guid)">
Comment thread
ShradhaGupta31 marked this conversation as resolved.
{{ getProductType(element) }}
</mat-cell>
</ng-container>
<!-- tags Column -->
<ng-container matColumnDef="tags">
<mat-header-cell *matHeaderCellDef mat-sort-header>
Expand Down Expand Up @@ -203,7 +217,7 @@ <h3 class="flex justify-center">
<mat-paginator
[pageSizeOptions]="[25, 50, 100]"
[pageSize]="pageEvent.pageSize"
[length]="totalCount()"
[length]="allCount"
(page)="pageChanged($event)"
showFirstLastButtons>
</mat-paginator>
Expand Down
96 changes: 96 additions & 0 deletions src/app/devices/devices.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,4 +250,100 @@ describe('DevicesComponent', () => {
component.tagFilterChange(matSelectChange)
expect(component.filteredTags()).toBe(mockValue)
})

describe('getProductType', () => {
it('should return ISM when bit 4 (0x10) is set', () => {
const device = { ...device01, deviceInfo: { fwSku: '16' } } as Device // 0x10 = 16
expect(component.getProductType(device)).toBe('ISM')
})

it('should return vPro when bit 3 (0x08) is set and bit 4 is not', () => {
const device = { ...device01, deviceInfo: { fwSku: '8' } } as Device // 0x08 = 8
expect(component.getProductType(device)).toBe('vPro')
})

it('should return ISM when both bit 4 and bit 3 are set (ISM takes priority)', () => {
const device = { ...device01, deviceInfo: { fwSku: '24' } } as Device // 0x18 = 24
expect(component.getProductType(device)).toBe('ISM')
})

it('should return empty string when neither bit is set', () => {
const device = { ...device01, deviceInfo: { fwSku: '4' } } as Device // 0x04 = 4
expect(component.getProductType(device)).toBe('')
})

it('should return empty string when fwSku is undefined', () => {
const device = { ...device01, deviceInfo: undefined } as Device
expect(component.getProductType(device)).toBe('')
})

it('should return empty string when fwSku is not a number', () => {
const device = { ...device01, deviceInfo: { fwSku: 'notanumber' } } as Device
expect(component.getProductType(device)).toBe('')
})
})

describe('onTabChange / applyTabFilter', () => {
beforeEach(() => {
const baseInfo = { fwVersion: '', fwBuild: '', fwSku: '0', features: '', ipAddress: '' }
component.allDevicesData = [
{ ...device01, deviceInfo: { ...baseInfo, currentMode: 'acm', discovered: false } },
{ ...device02, deviceInfo: { ...baseInfo, currentMode: 'not activated', discovered: true } }
]
})

it('should show all devices on tab 0', () => {
component.onTabChange(0)
expect(component.devices.data.length).toBe(2)
})

it('should filter to activated devices on tab 1', () => {
component.onTabChange(1)
expect(component.devices.data.length).toBe(1)
expect(component.devices.data[0].guid).toBe(device01.guid)
})

it('should filter to discovered devices on tab 2', () => {
component.onTabChange(2)
expect(component.devices.data.length).toBe(1)
expect(component.devices.data[0].guid).toBe(device02.guid)
})

it('should set totalCount to serverTotalCount on tab 0', () => {
;(component as any).serverTotalCount = 42
component.onTabChange(0)
expect(component.totalCount()).toBe(42)
})

it('should set totalCount to filtered length on tab 1', () => {
component.onTabChange(1)
expect(component.totalCount()).toBe(1)
})

it('should set totalCount to filtered length on tab 2', () => {
component.onTabChange(2)
expect(component.totalCount()).toBe(1)
})
})

describe('isNoData', () => {
it('should return false when allDevicesData has entries regardless of totalCount', () => {
component.allDevicesData = [device01]
component.isLoading.set(false)
component.totalCount.set(0) // filtered tab has 0 — should not trigger no-data
expect(component.isNoData()).toBeFalse()
})

it('should return true only when allDevicesData is empty and not loading', () => {
component.allDevicesData = []
component.isLoading.set(false)
expect(component.isNoData()).toBeTrue()
})

it('should return false when loading even if allDevicesData is empty', () => {
component.allDevicesData = []
component.isLoading.set(true)
expect(component.isNoData()).toBeFalse()
})
})
})
77 changes: 75 additions & 2 deletions src/app/devices/devices.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import { MatButton, MatIconButton } from '@angular/material/button'
import { MatToolbar } from '@angular/material/toolbar'
import { MatSort } from '@angular/material/sort'
import { MatInput } from '@angular/material/input'
import { MatTabGroup, MatTab } from '@angular/material/tabs'
import { TranslatePipe, TranslateService } from '@ngx-translate/core'

@Component({
Expand Down Expand Up @@ -86,6 +87,8 @@ import { TranslatePipe, TranslateService } from '@ngx-translate/core'
MatPaginator,
MatHint,
RouterModule,
MatTabGroup,
MatTab,
TranslatePipe
]
})
Expand All @@ -108,6 +111,62 @@ export class DevicesComponent implements OnInit, AfterViewInit {
public powerStates: any
public isCloudMode: boolean = environment.cloud

public activeTab = signal(0)
public allDevicesData: Device[] = []
private serverTotalCount = 0

get allCount(): number {
return this.serverTotalCount
}

get allTabLabel(): string {
return `${this.translate.instant('devices.tabs.all.value')} (${this.allCount})`
}

get activatedTabLabel(): string {
return `${this.translate.instant('devices.tabs.activated.value')} (${this.activatedCount})`
}

get discoveredTabLabel(): string {
return `${this.translate.instant('devices.tabs.discovered.value')} (${this.discoveredCount})`
}

get activatedCount(): number {
return this.allDevicesData.filter(
(d) => d.deviceInfo?.currentMode != null && d.deviceInfo.currentMode !== 'not activated'
).length
}

get discoveredCount(): number {
return this.allDevicesData.filter((d) => d.deviceInfo?.discovered === true).length
}

onTabChange(index: number): void {
this.activeTab.set(index)
this.applyTabFilter()
}

private applyTabFilter(): void {
let filtered: Device[]
switch (this.activeTab()) {
case 1:
filtered = this.allDevicesData.filter(
(d) => d.deviceInfo?.currentMode != null && d.deviceInfo.currentMode !== 'not activated'
)
this.totalCount.set(filtered.length)
break
case 2:
filtered = this.allDevicesData.filter((d) => d.deviceInfo?.discovered === true)
this.totalCount.set(filtered.length)
break
default:
filtered = this.allDevicesData
this.totalCount.set(this.serverTotalCount)
break
}
this.devices.data = filtered
}
Comment thread
ShradhaGupta31 marked this conversation as resolved.

get deleteDeviceLabel(): string {
return this.isCloudMode
? this.translate.instant('devices.actions.deactivateCloud.value')
Expand All @@ -119,6 +178,7 @@ export class DevicesComponent implements OnInit, AfterViewInit {
'hostname',
'guid',
'status',
'productType',
'tags',
'actions',
'notification'
Expand All @@ -140,6 +200,7 @@ export class DevicesComponent implements OnInit, AfterViewInit {
this.displayedColumns = [
'select',
'hostname',
'productType',
'tags',
'actions',
'notification'
Expand Down Expand Up @@ -211,6 +272,7 @@ export class DevicesComponent implements OnInit, AfterViewInit {
.pipe(
switchMap((res) => {
this.totalCount.set(res.totalCount)
this.serverTotalCount = res.totalCount

if (!environment.cloud) {
return of(res.data) // Return as-is for non-cloud
Expand Down Expand Up @@ -251,7 +313,8 @@ export class DevicesComponent implements OnInit, AfterViewInit {
})
)
.subscribe((devices) => {
this.devices.data = devices
this.allDevicesData = devices
this.applyTabFilter()

// Restore selection state on data retrieval
this.selectedDevices.clear()
Expand Down Expand Up @@ -335,13 +398,23 @@ export class DevicesComponent implements OnInit, AfterViewInit {
}

isNoData(): boolean {
return !this.isLoading() && this.totalCount() === 0
return !this.isLoading() && this.allDevicesData.length === 0
}

async navigateTo(path: string): Promise<void> {
await this.router.navigate([`/devices/${path}`])
}

getProductType(device: Device): string {
const skuNum = parseInt(device.deviceInfo?.fwSku ?? '', 10)
if (isNaN(skuNum)) return ''
const isISM = (skuNum & 0x10) > 0
const isVPro = (skuNum & 0x08) > 0
if (isISM) return 'ISM'
if (isVPro) return 'vPro'
return ''
}
Comment thread
ShradhaGupta31 marked this conversation as resolved.

translateConnectionStatus(status?: boolean): string {
switch (status) {
case false:
Expand Down
16 changes: 16 additions & 0 deletions src/assets/i18n/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -1065,10 +1065,26 @@
"description": "عنوان عمود الجدول لحالة الجهاز",
"value": "الحالة"
},
"devices.table.productType": {
"description": "Table column header for product type",
"value": "نوع المنتج"
},
"devices.table.tags": {
"description": "عنوان عمود الجدول للعلامات",
"value": "العلامات"
},
"devices.tabs.all": {
"description": "Tab label for all devices",
"value": "الكل"
},
"devices.tabs.activated": {
"description": "Tab label for activated devices",
"value": "مُفعَّل"
},
"devices.tabs.discovered": {
"description": "Tab label for discovered devices",
"value": "مكتشَف"
},
"deviceToolbar.power.on": {
"description": "تلميح الطاقة للحالة تشغيل",
"value": "الطاقة: تشغيل"
Expand Down
16 changes: 16 additions & 0 deletions src/assets/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -1065,10 +1065,26 @@
"description": "Tabellen-Spaltenüberschrift für Gerätestatus",
"value": "Status"
},
"devices.table.productType": {
"description": "Table column header for product type",
"value": "Produkttyp"
},
"devices.table.tags": {
"description": "Tabellen-Spaltenüberschrift für Tags",
"value": "Tags"
},
"devices.tabs.all": {
"description": "Tab label for all devices",
"value": "Alle"
},
"devices.tabs.activated": {
"description": "Tab label for activated devices",
"value": "Aktiviert"
},
"devices.tabs.discovered": {
"description": "Tab label for discovered devices",
"value": "Entdeckt"
},
"deviceToolbar.power.on": {
"description": "Energie-Tooltip für Eingeschaltet",
"value": "Strom: Ein"
Expand Down
20 changes: 16 additions & 4 deletions src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1237,10 +1237,26 @@
"description": "Table column header for device status",
"value": "Status"
},
"devices.table.productType": {
"description": "Table column header for product type",
"value": "Product Type"
},
"devices.table.tags": {
"description": "Table column header for tags",
"value": "Tags"
},
"devices.tabs.all": {
"description": "Tab label for all devices",
"value": "All"
},
"devices.tabs.activated": {
"description": "Tab label for activated devices",
"value": "Activated"
},
"devices.tabs.discovered": {
"description": "Tab label for discovered devices",
"value": "Discovered"
},
"deviceToolbar.power.on": {
"description": "Power tooltip for On",
"value": "Power: On"
Expand All @@ -1253,10 +1269,6 @@
"description": "Power tooltip for Off",
"value": "Power: Off"
},
"deviceToolbar.power.refreshAriaLabel": {
"description": "Aria label for the refresh power status button",
"value": "Refresh power status"
},
"deviceUserConsent.description": {
"description": "Description for user consent for devices",
"value": "A user consent code generated by Intel AMT is required to access the system."
Expand Down
Loading
Loading