Browse Source

UI: Added search widget

pull/4205/head
Vladyslav_Prykhodko 5 years ago
parent
commit
b3b9701076
  1. 3
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html
  2. 5
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts
  3. 50
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.html
  4. 99
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.ts
  5. 17
      ui-ngx/src/app/shared/models/widget.models.ts
  6. 3
      ui-ngx/src/assets/locale/locale.constant-en_US.json

3
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html

@ -253,10 +253,9 @@
(backStateDetails)="widgetBundleSelected(null)">
<div class="header-pane" *ngIf="isAddingWidget">
<div fxLayout="row">
<!-- <span class="tb-details-subtitle">{{ 'widgets-bundle.current' | translate }}</span>-->
<tb-widgets-bundle-search fxFlex
[(ngModel)]="searchBundle"
[placeholder]="!widgetsBundle?.title ? 'Search widgets bundle' : 'Search widget'"
placeholder="{{ (!widgetsBundle?.title ? 'widgets-bundle.search' : 'widget.search') | translate }}"
(ngModelChange)="searchBundle = $event">
</tb-widgets-bundle-search>
</div>

5
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts

@ -58,7 +58,7 @@ import { BreakpointObserver, BreakpointState } from '@angular/cdk/layout';
import { MediaBreakpoints } from '@shared/models/constants';
import { AuthUser } from '@shared/models/user.model';
import { getCurrentAuthState } from '@core/auth/auth.selectors';
import { Widget, WidgetConfig, WidgetPosition, widgetTypesData } from '@app/shared/models/widget.models';
import { Widget, WidgetConfig, WidgetInfo, WidgetPosition, widgetTypesData } from '@app/shared/models/widget.models';
import { environment as env } from '@env/environment';
import { Authority } from '@shared/models/authority.enum';
import { DialogService } from '@core/services/dialog.service';
@ -857,7 +857,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
}
}
addWidgetFromType(widget: Widget) {
addWidgetFromType(widget: WidgetInfo) {
this.onAddWidgetClosed();
this.widgetComponentService.getWidgetInfo(widget.bundleAlias, widget.typeAlias, widget.isSystemType).subscribe(
(widgetTypeInfo) => {
@ -1121,5 +1121,6 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
widgetBundleSelected(bundle: WidgetsBundle){
this.widgetsBundle = bundle;
this.searchBundle = '';
}
}

50
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.html

@ -16,9 +16,9 @@
-->
<div class="widget-select">
<div *ngIf="hasWidgetTypes()">
<div fxFlexFill fxLayoutGap="12px grid" fxLayout="row wrap">
<div *ngFor="let widget of widgets" class="mat-card-container">
<div *ngIf="widgetsBundle; else bundles">
<div *ngIf="(widgets$ | async)?.length; else emptyBundle" fxFlexFill fxLayoutGap="12px grid" fxLayout="row wrap">
<div *ngFor="let widget of widgets$ | async" class="mat-card-container">
<mat-card fxFlexFill fxLayout="row" fxLayoutGap="16px" (click)="onWidgetClicked($event, widget)">
<div class="preview-container" fxFlex="45">
<img class="preview" [src]="getPreviewImage(widget.image)" alt="{{ widget.title }}">
@ -26,32 +26,36 @@
<div fxFlex fxLayout="column">
<mat-card-title>{{widget.title}}</mat-card-title>
<mat-card-subtitle>{{ 'widget.' + widget.type | translate }}</mat-card-subtitle>
<mat-card-content *ngIf="widgetsBundle.description">
<mat-card-content *ngIf="widget.description">
{{ widget.description }}
</mat-card-content>
</div>
</mat-card>
</div>
</div>
<ng-template #emptyBundle>
<span translate
style="display: flex;"
fxLayoutAlign="center center"
class="mat-headline tb-absolute-fill">widgets-bundle.empty</span>
</ng-template>
</div>
<span translate *ngIf="widgetsBundle && !hasWidgetTypes()"
style="display: flex;"
fxLayoutAlign="center center"
class="mat-headline">widgets-bundle.empty</span>
<div *ngIf="!widgetsBundle" fxFlexFill fxLayoutGap="12px grid" fxLayout="row wrap">
<div *ngFor="let widgetsBundle of widgetsBundles$ | async" class="mat-card-container">
<mat-card fxFlexFill fxLayout="row" fxLayoutGap="16px" (click)="selectBundle($event, widgetsBundle)">
<div class="preview-container" fxFlex="45">
<img class="preview" [src]=getPreviewImage(widgetsBundle.image) alt="{{ widgetsBundle.title }}">
</div>
<div fxFlex fxLayout="column">
<mat-card-title>{{ widgetsBundle.title }}</mat-card-title>
<mat-card-subtitle *ngIf="isSystem(widgetsBundle)" translate>widgets-bundle.system</mat-card-subtitle>
<mat-card-content *ngIf="widgetsBundle.description">
{{ widgetsBundle.description }}
</mat-card-content>
</div>
</mat-card>
<ng-template #bundles>
<div fxFlexFill fxLayoutGap="12px grid" fxLayout="row wrap">
<div *ngFor="let widgetsBundle of widgetsBundles$ | async" class="mat-card-container">
<mat-card fxFlexFill fxLayout="row" fxLayoutGap="16px" (click)="selectBundle($event, widgetsBundle)">
<div class="preview-container" fxFlex="45">
<img class="preview" [src]=getPreviewImage(widgetsBundle.image) alt="{{ widgetsBundle.title }}">
</div>
<div fxFlex fxLayout="column">
<mat-card-title>{{ widgetsBundle.title }}</mat-card-title>
<mat-card-subtitle *ngIf="isSystem(widgetsBundle)" translate>widgets-bundle.system</mat-card-subtitle>
<mat-card-content *ngIf="widgetsBundle.description">
{{ widgetsBundle.description }}
</mat-card-content>
</div>
</mat-card>
</div>
</div>
</div>
</ng-template>
</div>

99
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.ts

@ -14,14 +14,14 @@
/// limitations under the License.
///
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { WidgetsBundle } from '@shared/models/widgets-bundle.model';
import { IAliasController } from '@core/api/widget-api.models';
import { NULL_UUID } from '@shared/models/id/has-uuid';
import { WidgetService } from '@core/http/widget.service';
import { Widget } from '@shared/models/widget.models';
import { WidgetInfo } from '@shared/models/widget.models';
import { toWidgetInfo } from '@home/models/widget-component.models';
import { distinctUntilChanged, map, mergeMap, publishReplay, refCount, share } from 'rxjs/operators';
import { distinctUntilChanged, map, publishReplay, refCount, switchMap } from 'rxjs/operators';
import { BehaviorSubject, Observable, of } from 'rxjs';
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
import { isDefinedAndNotNull } from '@core/utils';
@ -31,12 +31,24 @@ import { isDefinedAndNotNull } from '@core/utils';
templateUrl: './dashboard-widget-select.component.html',
styleUrls: ['./dashboard-widget-select.component.scss']
})
export class DashboardWidgetSelectComponent implements OnInit, OnChanges {
export class DashboardWidgetSelectComponent implements OnInit {
private search$ = new BehaviorSubject<string>('');
private widgetsTypes: Observable<Array<WidgetInfo>>;
private widgetsBundleValue: WidgetsBundle;
widgets$: Observable<Array<WidgetInfo>>;
widgetsBundles$: Observable<Array<WidgetsBundle>>;
@Input()
widgetsBundle: WidgetsBundle;
set widgetsBundle(widgetBundle: WidgetsBundle) {
this.widgetsTypes = null;
this.widgetsBundleValue = widgetBundle;
}
get widgetsBundle(): WidgetsBundle {
return this.widgetsBundleValue;
}
@Input()
aliasController: IAliasController;
@ -47,17 +59,11 @@ export class DashboardWidgetSelectComponent implements OnInit, OnChanges {
}
@Output()
widgetSelected: EventEmitter<Widget> = new EventEmitter<Widget>();
widgetSelected: EventEmitter<WidgetInfo> = new EventEmitter<WidgetInfo>();
@Output()
widgetsBundleSelected: EventEmitter<WidgetsBundle> = new EventEmitter<WidgetsBundle>();
widgets: Array<Widget> = [];
widgetsBundles$: Observable<Array<WidgetsBundle>>;
widgets$: Observable<Array<Widget>>;
constructor(private widgetsService: WidgetService,
private sanitizer: DomSanitizer) {
}
@ -65,66 +71,45 @@ export class DashboardWidgetSelectComponent implements OnInit, OnChanges {
ngOnInit(): void {
this.widgetsBundles$ = this.search$.asObservable().pipe(
distinctUntilChanged(),
mergeMap(search => this.fetchWidgetBundle(search))
switchMap(search => this.fetchWidgetBundle(search))
);
this.widgets$ = this.search$.asObservable().pipe(
distinctUntilChanged(),
mergeMap(search => this.fetchWidget(search))
switchMap(search => this.fetchWidget(search))
);
}
ngOnChanges(changes: SimpleChanges): void {
for (const propName of Object.keys(changes)) {
const change = changes[propName];
if (change.currentValue !== change.previousValue && (change.currentValue || change.currentValue === null)) {
if (propName === 'widgetsBundle') {
this.loadLibrary();
}
}
}
}
private loadLibrary() {
this.widgets.length = 0;
if (this.widgetsBundle !== null) {
const bundleAlias = this.widgetsBundle.alias;
const isSystem = this.widgetsBundle.tenantId.id === NULL_UUID;
this.widgetsService.getBundleWidgetTypes(bundleAlias,
isSystem).subscribe(
(types) => {
types = types.sort((a, b) => b.createdTime - a.createdTime);
let top = 0;
types.forEach((type) => {
private getWidgets(): Observable<Array<WidgetInfo>> {
if (!this.widgetsTypes) {
if (this.widgetsBundle !== null) {
const bundleAlias = this.widgetsBundle.alias;
const isSystem = this.widgetsBundle.tenantId.id === NULL_UUID;
this.widgetsTypes = this.widgetsService.getBundleWidgetTypes(bundleAlias, isSystem).pipe(
map(widgets => widgets.sort((a, b) => b.createdTime - a.createdTime)),
map(widgets => widgets.map((type) => {
const widgetTypeInfo = toWidgetInfo(type);
const widget: Widget = {
typeId: type.id,
const widget: WidgetInfo = {
isSystemType: isSystem,
bundleAlias,
typeAlias: widgetTypeInfo.alias,
type: widgetTypeInfo.type,
title: widgetTypeInfo.widgetName,
image: widgetTypeInfo.image,
description: widgetTypeInfo.description,
sizeX: widgetTypeInfo.sizeX,
sizeY: widgetTypeInfo.sizeY,
row: top,
col: 0,
config: JSON.parse(widgetTypeInfo.defaultConfig)
description: widgetTypeInfo.description
};
widget.config.title = widgetTypeInfo.widgetName;
this.widgets.push(widget);
top += widget.sizeY;
});
}
);
return widget;
})),
publishReplay(1),
refCount()
);
} else {
this.widgetsTypes = of([]);
}
}
return this.widgetsTypes;
}
hasWidgetTypes(): boolean {
return this.widgets.length > 0;
}
onWidgetClicked($event: Event, widget: Widget): void {
onWidgetClicked($event: Event, widget: WidgetInfo): void {
this.widgetSelected.emit(widget);
}
@ -164,8 +149,8 @@ export class DashboardWidgetSelectComponent implements OnInit, OnChanges {
);
}
private fetchWidget(search: string): Observable<Array<Widget>> {
return of(this.widgets).pipe(
private fetchWidget(search: string): Observable<Array<WidgetInfo>> {
return this.getWidgets().pipe(
map(widgets => search ? widgets.filter(
widget => (
widget.title?.toLowerCase().includes(search.toLowerCase()) ||

17
ui-ngx/src/app/shared/models/widget.models.ts

@ -401,21 +401,24 @@ export interface WidgetConfig {
[key: string]: any;
}
export interface Widget {
id?: string;
export interface Widget extends WidgetInfo{
typeId?: WidgetTypeId;
sizeX: number;
sizeY: number;
row: number;
col: number;
config: WidgetConfig;
}
export interface WidgetInfo {
id?: string;
isSystemType: boolean;
bundleAlias: string;
typeAlias: string;
type: widgetType;
title: string;
sizeX: number;
sizeY: number;
row: number;
col: number;
image: string;
description: string;
config: WidgetConfig;
}
export interface GroupInfo {

3
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -2264,7 +2264,8 @@
"export": "Export widget",
"no-data": "No data to display on widget",
"data-overflow": "Widget displays {{count}} out of {{total}} entities",
"alarm-data-overflow": "Widget displays alarms for {{allowedEntities}} (maximum allowed) entities out of {{totalEntities}} entities"
"alarm-data-overflow": "Widget displays alarms for {{allowedEntities}} (maximum allowed) entities out of {{totalEntities}} entities",
"search": "Search widget"
},
"widget-action": {
"header-button": "Widget header button",

Loading…
Cancel
Save