Skip to content

Commit

Permalink
NAS-130980 / 25.04 / ElectricEel HA Dashboard is missing the Standby …
Browse files Browse the repository at this point in the history
…System Information card. (#10640)
  • Loading branch information
AlexKarpov98 authored Sep 10, 2024
1 parent 4598a0c commit 4efc5ea
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
import { WidgetGroupComponent } from 'app/pages/dashboard/components/widget-group/widget-group.component';
import { WidgetGroupFormComponent } from 'app/pages/dashboard/components/widget-group-form/widget-group-form.component';
import { DashboardStore } from 'app/pages/dashboard/services/dashboard.store';
import { defaultWidgets } from 'app/pages/dashboard/services/default-widgets.constant';
import { getDefaultWidgets } from 'app/pages/dashboard/services/get-default-widgets';
import { WidgetGroup, WidgetGroupLayout } from 'app/pages/dashboard/types/widget-group.interface';
import { ChainedComponentResponse, IxChainedSlideInService } from 'app/services/ix-chained-slide-in.service';

Expand Down Expand Up @@ -160,7 +160,7 @@ describe('DashboardComponent', () => {
const saveButton = await loader.getHarness(MatButtonHarness.with({ text: 'Save' }));
await saveButton.click();

expect(spectator.inject(DashboardStore).save).toHaveBeenCalledWith(defaultWidgets);
expect(spectator.inject(DashboardStore).save).toHaveBeenCalledWith(getDefaultWidgets());
expect(spectator.inject(SnackbarService).success).toHaveBeenCalledWith('Dashboard settings saved');
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { Store } from '@ngrx/store';
import { TranslateService } from '@ngx-translate/core';
import { isEqual } from 'lodash-es';
import { EmptyType } from 'app/enums/empty-type.enum';
Expand All @@ -15,10 +16,12 @@ import { SnackbarService } from 'app/modules/snackbar/services/snackbar.service'
import { dashboardElements } from 'app/pages/dashboard/components/dashboard/dashboard.elements';
import { WidgetGroupFormComponent } from 'app/pages/dashboard/components/widget-group-form/widget-group-form.component';
import { DashboardStore } from 'app/pages/dashboard/services/dashboard.store';
import { defaultWidgets } from 'app/pages/dashboard/services/default-widgets.constant';
import { getDefaultWidgets } from 'app/pages/dashboard/services/get-default-widgets';
import { WidgetGroup } from 'app/pages/dashboard/types/widget-group.interface';
import { ErrorHandlerService } from 'app/services/error-handler.service';
import { ChainedComponentResponse, IxChainedSlideInService } from 'app/services/ix-chained-slide-in.service';
import { AppsState } from 'app/store';
import { selectIsHaLicensed } from 'app/store/ha-info/ha-info.selectors';

@UntilDestroy()
@Component({
Expand All @@ -43,13 +46,14 @@ export class DashboardComponent implements OnInit {
readonly renderedGroups = signal<WidgetGroup[]>([]);
readonly savedGroups = toSignal(this.dashboardStore.groups$);
readonly isLoading = toSignal(this.dashboardStore.isLoading$);
readonly isHaLicensed = toSignal(this.store$.select(selectIsHaLicensed));
readonly isLoadingFirstTime = computed(() => this.isLoading() && this.savedGroups() === null);
readonly newFeatureConfig = {
key: 'dashboardConfigure',
message: this.translate.instant('New widgets and layouts.'),
};
readonly customLayout = computed(() => {
return !isEqual(this.renderedGroups(), defaultWidgets);
return !isEqual(this.renderedGroups(), getDefaultWidgets(this.isHaLicensed()));
});

emptyDashboardConf: EmptyConfig = {
Expand All @@ -66,6 +70,7 @@ export class DashboardComponent implements OnInit {
private errorHandler: ErrorHandlerService,
private translate: TranslateService,
private snackbar: SnackbarService,
private store$: Store<AppsState>,
) {}

ngOnInit(): void {
Expand Down Expand Up @@ -142,7 +147,7 @@ export class DashboardComponent implements OnInit {
}

protected onReset(): void {
this.renderedGroups.set(defaultWidgets);
this.renderedGroups.set(getDefaultWidgets(this.isHaLicensed()));
this.snackbar.success(this.translate.instant('Default widgets restored'));
}

Expand Down
14 changes: 12 additions & 2 deletions src/app/pages/dashboard/services/dashboard.store.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { createServiceFactory, SpectatorService, mockProvider } from '@ngneat/spectator/jest';
import { provideMockStore } from '@ngrx/store/testing';
import { firstValueFrom, of } from 'rxjs';
import { mockCall, mockWebSocket } from 'app/core/testing/utils/mock-websocket.utils';
import { defaultWidgets } from 'app/pages/dashboard/services/default-widgets.constant';
import { getDefaultWidgets } from 'app/pages/dashboard/services/get-default-widgets';
import { WidgetGroupLayout } from 'app/pages/dashboard/types/widget-group.interface';
import { WidgetType } from 'app/pages/dashboard/types/widget.interface';
import { AuthService } from 'app/services/auth/auth.service';
import { WebSocketService } from 'app/services/ws.service';
import { selectIsHaLicensed } from 'app/store/ha-info/ha-info.selectors';
import { DashboardStore, initialState } from './dashboard.store';

const initialGroups = [
Expand Down Expand Up @@ -35,6 +37,14 @@ describe('DashboardStore', () => {
},
}),
}),
provideMockStore({
selectors: [
{
selector: selectIsHaLicensed,
value: true,
},
],
}),
mockWebSocket([
mockCall('auth.set_attribute'),
]),
Expand Down Expand Up @@ -105,7 +115,7 @@ describe('DashboardStore', () => {

expect(await firstValueFrom(spectator.service.state$)).toMatchObject({
...initialState,
groups: defaultWidgets,
groups: getDefaultWidgets(true),
});
});

Expand Down
23 changes: 15 additions & 8 deletions src/app/pages/dashboard/services/dashboard.store.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { Injectable } from '@angular/core';
import { UntilDestroy } from '@ngneat/until-destroy';
import { ComponentStore } from '@ngrx/component-store';
import { Store } from '@ngrx/store';
import {
EMPTY,
Observable, catchError, filter, finalize, map, of, switchMap, tap,
Observable, catchError, combineLatest, filter, finalize, map, of, switchMap, tap,
} from 'rxjs';
import { WidgetName } from 'app/enums/widget-name.enum';
import { defaultWidgets } from 'app/pages/dashboard/services/default-widgets.constant';
import { getDefaultWidgets } from 'app/pages/dashboard/services/get-default-widgets';
import { WidgetGroup, WidgetGroupLayout } from 'app/pages/dashboard/types/widget-group.interface';
import { SomeWidgetSettings, WidgetType } from 'app/pages/dashboard/types/widget.interface';
import { AuthService } from 'app/services/auth/auth.service';
import { ErrorHandlerService } from 'app/services/error-handler.service';
import { WebSocketService } from 'app/services/ws.service';
import { AppsState } from 'app/store';
import { selectIsHaLicensed } from 'app/store/ha-info/ha-info.selectors';

// we have external `DashConfigItem` in old dashboard, but it will be removed once we go ahead with new dashboard
export interface OldDashboardConfigItem {
Expand Down Expand Up @@ -50,6 +53,7 @@ export class DashboardStore extends ComponentStore<DashboardState> {
private authService: AuthService,
private ws: WebSocketService,
private errorHandler: ErrorHandlerService,
private store$: Store<AppsState>,
) {
super(initialState);
}
Expand All @@ -64,15 +68,18 @@ export class DashboardStore extends ComponentStore<DashboardState> {
}
return this.authService.refreshUser();
}),
switchMap(() => this.authService.user$.pipe(
filter(Boolean),
map((user) => user.attributes.dashState),
)),
tap((dashState) => {
switchMap(() => combineLatest([
this.authService.user$.pipe(
filter(Boolean),
map((user) => user.attributes.dashState),
),
this.store$.select(selectIsHaLicensed),
])),
tap(([dashState, isHaLicensed]) => {
this.setState({
isLoading: false,
globalError: '',
groups: this.getDashboardGroups(dashState || defaultWidgets),
groups: this.getDashboardGroups(dashState || getDefaultWidgets(isHaLicensed)),
});
}),
catchError((error) => {
Expand Down
21 changes: 21 additions & 0 deletions src/app/pages/dashboard/services/get-default-widgets.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { getDefaultWidgets } from 'app/pages/dashboard/services/get-default-widgets';
import { WidgetGroup } from 'app/pages/dashboard/types/widget-group.interface';
import { WidgetType } from 'app/pages/dashboard/types/widget.interface';

describe('getDefaultWidgets', () => {
it('should return all default widgets when isHaLicensed is true', () => {
const result: WidgetGroup[] = getDefaultWidgets(true);

expect(result).toHaveLength(9);
expect(result[0].slots[0].type).toBe(WidgetType.SystemInfoActive);
expect(result[1].slots[0].type).toBe(WidgetType.SystemInfoPassive);
});

it('should return default widgets without the second widget when isHaLicensed is false', () => {
const result: WidgetGroup[] = getDefaultWidgets(false);

expect(result).toHaveLength(8);
expect(result[0].slots[0].type).toBe(WidgetType.SystemInfoActive);
expect(result[1].slots[0].type).toBe(WidgetType.CpuModelWidget);
});
});
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import { WidgetGroup, WidgetGroupLayout } from 'app/pages/dashboard/types/widget-group.interface';
import { WidgetType } from 'app/pages/dashboard/types/widget.interface';

export const defaultWidgets: WidgetGroup[] = [
const defaultWidgets: WidgetGroup[] = [
{
layout: WidgetGroupLayout.Full,
slots: [
{ type: WidgetType.SystemInfoActive },
],
},
{
layout: WidgetGroupLayout.Full,
slots: [
{ type: WidgetType.SystemInfoPassive },
],
},
{
layout: WidgetGroupLayout.Halves,
slots: [
Expand Down Expand Up @@ -54,3 +60,13 @@ export const defaultWidgets: WidgetGroup[] = [
],
},
];

export const getDefaultWidgets = (isHaLicensed?: boolean): WidgetGroup[] => {
if (!isHaLicensed) {
return defaultWidgets.filter(
(widgetGroup) => !widgetGroup.slots.some((slot) => slot.type === WidgetType.SystemInfoPassive),
);
}

return defaultWidgets;
};

0 comments on commit 4efc5ea

Please sign in to comment.