Browse Source

Widgets library import export support.

pull/2175/head
Igor Kulikov 7 years ago
parent
commit
65b7c139cf
  1. 2
      application/src/main/data/json/system/widget_bundles/charts.json
  2. 55
      ui-ngx/src/app/core/http/widget.service.ts
  3. 8
      ui-ngx/src/app/modules/home/components/import-export/import-export.models.ts
  4. 157
      ui-ngx/src/app/modules/home/components/import-export/import-export.service.ts
  5. 2
      ui-ngx/src/app/modules/home/models/dashboard-component.models.ts
  6. 50
      ui-ngx/src/app/modules/home/pages/widget/widget-library-routing.module.ts
  7. 36
      ui-ngx/src/app/modules/home/pages/widget/widget-library.component.ts
  8. 17
      ui-ngx/src/app/modules/home/pages/widget/widgets-bundles-table-config.resolver.ts
  9. 4
      ui-ngx/src/scss/mixins.scss

2
application/src/main/data/json/system/widget_bundles/charts.json

@ -55,7 +55,7 @@
], ],
"templateHtml": "<canvas id=\"pieChart\"></canvas>\n", "templateHtml": "<canvas id=\"pieChart\"></canvas>\n",
"templateCss": "", "templateCss": "",
"controllerScript": "self.onInit = function() {\n var pieData = {\n labels: [],\n datasets: []\n };\n\n var dataset = {\n data: [],\n backgroundColor: [],\n borderColor: [],\n borderWidth: [],\n hoverBackgroundColor: []\n }\n \n var borderColor = self.ctx.settings.borderColor || '#fff';\n var borderWidth = angular.isDefined(self.ctx.settings.borderWidth) ? self.ctx.settings.borderWidth : 5;\n \n pieData.datasets.push(dataset);\n \n for (var i=0; i < self.ctx.data.length; i++) {\n var dataKey = self.ctx.data[i].dataKey;\n pieData.labels.push(dataKey.label);\n dataset.data.push(0);\n var hoverBackgroundColor = tinycolor(dataKey.color).lighten(15);\n dataset.backgroundColor.push(dataKey.color);\n dataset.borderColor.push(borderColor);\n dataset.borderWidth.push(borderWidth);\n dataset.hoverBackgroundColor.push(hoverBackgroundColor.toRgbString());\n }\n\n var options = {\n responsive: false,\n maintainAspectRatio: false,\n legend: {\n display: true,\n labels: {\n fontColor: '#666'\n }\n },\n tooltips: {\n callbacks: {\n label: function(tooltipItem, data) {\n var label = data.labels[tooltipItem.index];\n var value = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];\n var content = label + ': ' + value;\n var units = self.ctx.settings.units ? self.ctx.settings.units : self.ctx.units;\n if (units) {\n content += ' ' + units;\n } \n return content;\n }\n }\n }\n };\n\n if (self.ctx.settings.legend) {\n options.legend.display = self.ctx.settings.legend.display !== false;\n options.legend.labels.fontColor = self.ctx.settings.legend.labelsFontColor || '#666';\n }\n\n var ctx = $('#pieChart', self.ctx.$container);\n self.ctx.chart = new Chart(ctx, {\n type: 'doughnut',\n data: pieData,\n options: options\n });\n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.data.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length - 1];\n var value = tvPair[1];\n self.ctx.chart.data.datasets[0].data[i] = parseFloat(value);\n }\n }\n self.ctx.chart.update();\n}\n\nself.onResize = function() {\n self.ctx.chart.resize();\n}\n\n", "controllerScript": "self.onInit = function() {\n var pieData = {\n labels: [],\n datasets: []\n };\n\n var dataset = {\n data: [],\n backgroundColor: [],\n borderColor: [],\n borderWidth: [],\n hoverBackgroundColor: []\n }\n \n var borderColor = self.ctx.settings.borderColor || '#fff';\n var borderWidth = typeof self.ctx.settings.borderWidth !== 'undefined' ? self.ctx.settings.borderWidth : 5;\n \n pieData.datasets.push(dataset);\n \n for (var i=0; i < self.ctx.data.length; i++) {\n var dataKey = self.ctx.data[i].dataKey;\n pieData.labels.push(dataKey.label);\n dataset.data.push(0);\n var hoverBackgroundColor = tinycolor(dataKey.color).lighten(15);\n dataset.backgroundColor.push(dataKey.color);\n dataset.borderColor.push(borderColor);\n dataset.borderWidth.push(borderWidth);\n dataset.hoverBackgroundColor.push(hoverBackgroundColor.toRgbString());\n }\n\n var options = {\n responsive: false,\n maintainAspectRatio: false,\n legend: {\n display: true,\n labels: {\n fontColor: '#666'\n }\n },\n tooltips: {\n callbacks: {\n label: function(tooltipItem, data) {\n var label = data.labels[tooltipItem.index];\n var value = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];\n var content = label + ': ' + value;\n var units = self.ctx.settings.units ? self.ctx.settings.units : self.ctx.units;\n if (units) {\n content += ' ' + units;\n } \n return content;\n }\n }\n }\n };\n\n if (self.ctx.settings.legend) {\n options.legend.display = self.ctx.settings.legend.display !== false;\n options.legend.labels.fontColor = self.ctx.settings.legend.labelsFontColor || '#666';\n }\n\n var ctx = $('#pieChart', self.ctx.$container);\n self.ctx.chart = new Chart(ctx, {\n type: 'doughnut',\n data: pieData,\n options: options\n });\n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.data.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length - 1];\n var value = tvPair[1];\n self.ctx.chart.data.datasets[0].data[i] = parseFloat(value);\n }\n }\n self.ctx.chart.update();\n}\n\nself.onResize = function() {\n self.ctx.chart.resize();\n}\n\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"borderWidth\": {\n \"title\": \"Border width\",\n \"type\": \"number\",\n \"default\": 5\n },\n \"borderColor\": {\n \"title\": \"Border color\",\n \"type\": \"string\",\n \"default\": \"#fff\"\n },\n \"legend\": {\n \"title\": \"Legend settings\",\n \"type\": \"object\",\n \"properties\": {\n \"display\": {\n \"title\": \"Display legend\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"labelsFontColor\": {\n \"title\": \"Labels font color\",\n \"type\": \"string\",\n \"default\": \"#666\"\n }\n }\n }\n },\n \"required\": []\n },\n \"form\": [\n \"borderWidth\", \n {\n \"key\": \"borderColor\",\n \"type\": \"color\"\n }, \n {\n \"key\": \"legend\",\n \"items\": [\n \"legend.display\",\n {\n \"key\": \"legend.labelsFontColor\",\n \"type\": \"color\"\n }\n ]\n }\n ]\n}", "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"borderWidth\": {\n \"title\": \"Border width\",\n \"type\": \"number\",\n \"default\": 5\n },\n \"borderColor\": {\n \"title\": \"Border color\",\n \"type\": \"string\",\n \"default\": \"#fff\"\n },\n \"legend\": {\n \"title\": \"Legend settings\",\n \"type\": \"object\",\n \"properties\": {\n \"display\": {\n \"title\": \"Display legend\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"labelsFontColor\": {\n \"title\": \"Labels font color\",\n \"type\": \"string\",\n \"default\": \"#666\"\n }\n }\n }\n },\n \"required\": []\n },\n \"form\": [\n \"borderWidth\", \n {\n \"key\": \"borderColor\",\n \"type\": \"color\"\n }, \n {\n \"key\": \"legend\",\n \"items\": [\n \"legend.display\",\n {\n \"key\": \"legend.labelsFontColor\",\n \"type\": \"color\"\n }\n ]\n }\n ]\n}",
"dataKeySettingsSchema": "{}\n", "dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#26a69a\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#f57c00\",\"settings\":{},\"_hash\":0.545701115289893,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#afb42b\",\"settings\":{},\"_hash\":0.2592906835158064,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#673ab7\",\"settings\":{},\"_hash\":0.12880275585455747,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"borderWidth\":5,\"borderColor\":\"#fff\",\"legend\":{\"display\":true,\"labelsFontColor\":\"#666666\"}},\"title\":\"Doughnut - Chart.js\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#26a69a\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#f57c00\",\"settings\":{},\"_hash\":0.545701115289893,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#afb42b\",\"settings\":{},\"_hash\":0.2592906835158064,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#673ab7\",\"settings\":{},\"_hash\":0.12880275585455747,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"borderWidth\":5,\"borderColor\":\"#fff\",\"legend\":{\"display\":true,\"labelsFontColor\":\"#666666\"}},\"title\":\"Doughnut - Chart.js\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"

55
ui-ngx/src/app/core/http/widget.service.ts

@ -21,7 +21,7 @@ import { HttpClient } from '@angular/common/http';
import { PageLink } from '@shared/models/page/page-link'; import { PageLink } from '@shared/models/page/page-link';
import { PageData } from '@shared/models/page/page-data'; import { PageData } from '@shared/models/page/page-data';
import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; import { WidgetsBundle } from '@shared/models/widgets-bundle.model';
import { WidgetType, widgetType, WidgetTypeData, widgetTypesData } from '@shared/models/widget.models'; import { Widget, WidgetType, widgetType, WidgetTypeData, widgetTypesData } from '@shared/models/widget.models';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { ResourcesService } from '../services/resources.service'; import { ResourcesService } from '../services/resources.service';
@ -119,6 +119,59 @@ export class WidgetService {
defaultHttpOptions(ignoreLoading, ignoreErrors)); defaultHttpOptions(ignoreLoading, ignoreErrors));
} }
public loadBundleLibraryWidgets(bundleAlias: string, isSystem: boolean,
ignoreErrors: boolean = false, ignoreLoading: boolean = false): Observable<Array<Widget>> {
return this.getBundleWidgetTypes(bundleAlias, isSystem, ignoreErrors, ignoreLoading).pipe(
map((types) => {
types = types.sort((a, b) => {
let result = widgetType[b.descriptor.type].localeCompare(widgetType[a.descriptor.type]);
if (result === 0) {
result = b.createdTime - a.createdTime;
}
return result;
});
const widgetTypes = new Array<Widget>();
let top = 0;
const lastTop = [0, 0, 0];
let col = 0;
let column = 0;
types.forEach((type) => {
const widgetTypeInfo = toWidgetInfo(type);
const sizeX = 8;
const sizeY = Math.floor(widgetTypeInfo.sizeY);
const widget: Widget = {
typeId: type.id,
isSystemType: isSystem,
bundleAlias,
typeAlias: widgetTypeInfo.alias,
type: widgetTypeInfo.type,
title: widgetTypeInfo.widgetName,
sizeX,
sizeY,
row: top,
col,
config: JSON.parse(widgetTypeInfo.defaultConfig)
};
widget.config.title = widgetTypeInfo.widgetName;
widgetTypes.push(widget);
top += sizeY;
if (top > lastTop[column] + 10) {
lastTop[column] = top;
column++;
if (column > 2) {
column = 0;
}
top = lastTop[column];
col = column * 8;
}
});
return widgetTypes;
})
);
}
public getWidgetType(bundleAlias: string, widgetTypeAlias: string, isSystem: boolean, public getWidgetType(bundleAlias: string, widgetTypeAlias: string, isSystem: boolean,
ignoreErrors: boolean = false, ignoreLoading: boolean = false): Observable<WidgetType> { ignoreErrors: boolean = false, ignoreLoading: boolean = false): Observable<WidgetType> {
return this.http.get<WidgetType>(`/api/widgetType?isSystem=${isSystem}&bundleAlias=${bundleAlias}&alias=${widgetTypeAlias}`, return this.http.get<WidgetType>(`/api/widgetType?isSystem=${isSystem}&bundleAlias=${bundleAlias}&alias=${widgetTypeAlias}`,

8
ui-ngx/src/app/modules/home/components/import-export/import-export.models.ts

@ -14,10 +14,16 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Widget } from '@app/shared/models/widget.models'; import { Widget, WidgetType } from '@app/shared/models/widget.models';
import { DashboardLayoutId } from '@shared/models/dashboard.models'; import { DashboardLayoutId } from '@shared/models/dashboard.models';
import { WidgetsBundle } from '@shared/models/widgets-bundle.model';
export interface ImportWidgetResult { export interface ImportWidgetResult {
widget: Widget; widget: Widget;
layoutId: DashboardLayoutId; layoutId: DashboardLayoutId;
} }
export interface WidgetsBundleItem {
widgetsBundle: WidgetsBundle;
widgetTypes: WidgetType[];
}

157
ui-ngx/src/app/modules/home/components/import-export/import-export.service.ts

@ -38,15 +38,18 @@ import { forkJoin, Observable, of } from 'rxjs';
import { catchError, map, mergeMap } from 'rxjs/operators'; import { catchError, map, mergeMap } from 'rxjs/operators';
import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
import { EntityService } from '@core/http/entity.service'; import { EntityService } from '@core/http/entity.service';
import { Widget, WidgetSize } from '@shared/models/widget.models'; import { Widget, WidgetSize, WidgetType } from '@shared/models/widget.models';
import { import {
EntityAliasesDialogComponent, EntityAliasesDialogComponent,
EntityAliasesDialogData EntityAliasesDialogData
} from '@home/components/alias/entity-aliases-dialog.component'; } from '@home/components/alias/entity-aliases-dialog.component';
import { ItemBufferService, WidgetItem } from '@core/services/item-buffer.service'; import { ItemBufferService, WidgetItem } from '@core/services/item-buffer.service';
import { ImportWidgetResult } from './import-export.models'; import { ImportWidgetResult, WidgetsBundleItem } from './import-export.models';
import { EntityType } from '@shared/models/entity-type.models'; import { EntityType } from '@shared/models/entity-type.models';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { WidgetService } from '@core/http/widget.service';
import { NULL_UUID } from '@shared/models/id/has-uuid';
import { WidgetsBundle } from '@shared/models/widgets-bundle.model';
@Injectable() @Injectable()
export class ImportExportService { export class ImportExportService {
@ -57,6 +60,7 @@ export class ImportExportService {
private translate: TranslateService, private translate: TranslateService,
private dashboardService: DashboardService, private dashboardService: DashboardService,
private dashboardUtils: DashboardUtilsService, private dashboardUtils: DashboardUtilsService,
private widgetService: WidgetService,
private entityService: EntityService, private entityService: EntityService,
private utils: UtilsService, private utils: UtilsService,
private itembuffer: ItemBufferService, private itembuffer: ItemBufferService,
@ -72,13 +76,7 @@ export class ImportExportService {
this.exportToPc(this.prepareDashboardExport(dashboard), name + '.json'); this.exportToPc(this.prepareDashboardExport(dashboard), name + '.json');
}, },
(e) => { (e) => {
let message = e; this.handleExportError(e, 'dashboard.export-failed-error');
if (!message) {
message = this.translate.instant('error.unknown-error');
}
this.store.dispatch(new ActionNotificationShow(
{message: this.translate.instant('dashboard.export-failed-error', {error: message}),
type: 'error'}));
} }
); );
} }
@ -223,6 +221,119 @@ export class ImportExportService {
); );
} }
public exportWidgetType(widgetTypeId: string) {
this.widgetService.getWidgetTypeById(widgetTypeId).subscribe(
(widgetType) => {
if (isDefined(widgetType.bundleAlias)) {
delete widgetType.bundleAlias;
}
let name = widgetType.name;
name = name.toLowerCase().replace(/\W/g, '_');
this.exportToPc(this.prepareExport(widgetType), name + '.json');
},
(e) => {
this.handleExportError(e, 'widget-type.export-failed-error');
}
);
}
public importWidgetType(bundleAlias: string): Observable<WidgetType> {
return this.openImportDialog('widget-type.import', 'widget-type.widget-type-file').pipe(
mergeMap((widgetType: WidgetType) => {
if (!this.validateImportedWidgetType(widgetType)) {
this.store.dispatch(new ActionNotificationShow(
{message: this.translate.instant('widget-type.invalid-widget-type-file-error'),
type: 'error'}));
throw new Error('Invalid widget type file');
} else {
widgetType.bundleAlias = bundleAlias;
return this.widgetService.saveImportedWidgetType(widgetType);
}
}),
catchError((err) => {
return of(null);
})
);
}
public exportWidgetsBundle(widgetsBundleId: string) {
this.widgetService.getWidgetsBundle(widgetsBundleId).subscribe(
(widgetsBundle) => {
const bundleAlias = widgetsBundle.alias;
const isSystem = widgetsBundle.tenantId.id === NULL_UUID;
this.widgetService.getBundleWidgetTypes(bundleAlias, isSystem).subscribe(
(widgetTypes) => {
const widgetsBundleItem: WidgetsBundleItem = {
widgetsBundle: this.prepareExport(widgetsBundle),
widgetTypes: []
};
for (const widgetType of widgetTypes) {
if (isDefined(widgetType.bundleAlias)) {
delete widgetType.bundleAlias;
}
widgetsBundleItem.widgetTypes.push(this.prepareExport(widgetType));
}
let name = widgetsBundle.title;
name = name.toLowerCase().replace(/\W/g, '_');
this.exportToPc(widgetsBundleItem, name + '.json');
},
(e) => {
this.handleExportError(e, 'widgets-bundle.export-failed-error');
}
);
},
(e) => {
this.handleExportError(e, 'widgets-bundle.export-failed-error');
}
);
}
public importWidgetsBundle(): Observable<WidgetsBundle> {
return this.openImportDialog('widgets-bundle.import', 'widgets-bundle.widgets-bundle-file').pipe(
mergeMap((widgetsBundleItem: WidgetsBundleItem) => {
if (!this.validateImportedWidgetsBundle(widgetsBundleItem)) {
this.store.dispatch(new ActionNotificationShow(
{message: this.translate.instant('widgets-bundle.invalid-widgets-bundle-file-error'),
type: 'error'}));
throw new Error('Invalid widgets bundle file');
} else {
const widgetsBundle = widgetsBundleItem.widgetsBundle;
return this.widgetService.saveWidgetsBundle(widgetsBundle).pipe(
mergeMap((savedWidgetsBundle) => {
const bundleAlias = savedWidgetsBundle.alias;
const widgetTypes = widgetsBundleItem.widgetTypes;
if (widgetTypes.length) {
const saveWidgetTypesObservables: Array<Observable<WidgetType>> = [];
for (const widgetType of widgetTypes) {
widgetType.bundleAlias = bundleAlias;
saveWidgetTypesObservables.push(this.widgetService.saveImportedWidgetType(widgetType));
}
return forkJoin(saveWidgetTypesObservables).pipe(
map(() => savedWidgetsBundle)
);
} else {
return of(savedWidgetsBundle);
}
}
));
}
}),
catchError((err) => {
return of(null);
})
);
}
private handleExportError(e: any, errorDetailsMessageId: string) {
let message = e;
if (!message) {
message = this.translate.instant('error.unknown-error');
}
this.store.dispatch(new ActionNotificationShow(
{message: this.translate.instant(errorDetailsMessageId, {error: message}),
type: 'error'}));
}
private validateImportedDashboard(dashboard: Dashboard): boolean { private validateImportedDashboard(dashboard: Dashboard): boolean {
if (isUndefined(dashboard.title) || isUndefined(dashboard.configuration)) { if (isUndefined(dashboard.title) || isUndefined(dashboard.configuration)) {
return false; return false;
@ -246,6 +357,34 @@ export class ImportExportService {
return true; return true;
} }
private validateImportedWidgetType(widgetType: WidgetType): boolean {
if (isUndefined(widgetType.name)
|| isUndefined(widgetType.descriptor)) {
return false;
}
return true;
}
private validateImportedWidgetsBundle(widgetsBundleItem: WidgetsBundleItem): boolean {
if (isUndefined(widgetsBundleItem.widgetsBundle)) {
return false;
}
if (isUndefined(widgetsBundleItem.widgetTypes)) {
return false;
}
const widgetsBundle = widgetsBundleItem.widgetsBundle;
if (isUndefined(widgetsBundle.title)) {
return false;
}
const widgetTypes = widgetsBundleItem.widgetTypes;
for (const widgetType of widgetTypes) {
if (!this.validateImportedWidgetType(widgetType)) {
return false;
}
}
return true;
}
private saveImportedDashboard(dashboard: Dashboard): Observable<Dashboard> { private saveImportedDashboard(dashboard: Dashboard): Observable<Dashboard> {
return this.dashboardService.saveDashboard(dashboard); return this.dashboardService.saveDashboard(dashboard);
} }

2
ui-ngx/src/app/modules/home/models/dashboard-component.models.ts

@ -114,7 +114,7 @@ export class DashboardWidgets implements Iterable<DashboardWidget> {
updateRecords.push({ updateRecords.push({
widget: added.item, widget: added.item,
widgetId: added.item.id, widgetId: added.item.id,
widgetLayout: this.widgetLayouts[added.item.id], widgetLayout: this.widgetLayouts ? this.widgetLayouts[added.item.id] : null,
operation: 'add' operation: 'add'
}); });
}); });

50
ui-ngx/src/app/modules/home/pages/widget/widget-library-routing.module.ts

@ -63,54 +63,10 @@ export class WidgetsTypesDataResolver implements Resolve<WidgetsData> {
const widgetsBundle: WidgetsBundle = route.parent.data.widgetsBundle; const widgetsBundle: WidgetsBundle = route.parent.data.widgetsBundle;
const bundleAlias = widgetsBundle.alias; const bundleAlias = widgetsBundle.alias;
const isSystem = widgetsBundle.tenantId.id === NULL_UUID; const isSystem = widgetsBundle.tenantId.id === NULL_UUID;
return this.widgetsService.getBundleWidgetTypes(bundleAlias, return this.widgetsService.loadBundleLibraryWidgets(bundleAlias,
isSystem).pipe( isSystem).pipe(
map((types) => { map((widgets) => {
types = types.sort((a, b) => { return { widgets };
let result = widgetType[b.descriptor.type].localeCompare(widgetType[a.descriptor.type]);
if (result === 0) {
result = b.createdTime - a.createdTime;
}
return result;
});
const widgetTypes = new Array<Widget>();
let top = 0;
const lastTop = [0, 0, 0];
let col = 0;
let column = 0;
types.forEach((type) => {
const widgetTypeInfo = toWidgetInfo(type);
const sizeX = 8;
const sizeY = Math.floor(widgetTypeInfo.sizeY);
const widget: Widget = {
typeId: type.id,
isSystemType: isSystem,
bundleAlias,
typeAlias: widgetTypeInfo.alias,
type: widgetTypeInfo.type,
title: widgetTypeInfo.widgetName,
sizeX,
sizeY,
row: top,
col,
config: JSON.parse(widgetTypeInfo.defaultConfig)
};
widget.config.title = widgetTypeInfo.widgetName;
widgetTypes.push(widget);
top += sizeY;
if (top > lastTop[column] + 10) {
lastTop[column] = top;
column++;
if (column > 2) {
column = 0;
}
top = lastTop[column];
col = column * 8;
}
});
return { widgets: widgetTypes };
} }
)); ));
} }

36
ui-ngx/src/app/modules/home/pages/widget/widget-library.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, OnInit, ViewChild } from '@angular/core'; import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
@ -24,7 +24,7 @@ import { WidgetsBundle } from '@shared/models/widgets-bundle.model';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { Authority } from '@shared/models/authority.enum'; import { Authority } from '@shared/models/authority.enum';
import { NULL_UUID } from '@shared/models/id/has-uuid'; import { NULL_UUID } from '@shared/models/id/has-uuid';
import { Observable, of } from 'rxjs'; import { Observable, of, Subscription } from 'rxjs';
import { Widget, widgetType } from '@app/shared/models/widget.models'; import { Widget, widgetType } from '@app/shared/models/widget.models';
import { WidgetService } from '@core/http/widget.service'; import { WidgetService } from '@core/http/widget.service';
import { map, mergeMap, share } from 'rxjs/operators'; import { map, mergeMap, share } from 'rxjs/operators';
@ -42,6 +42,7 @@ import { DeviceCredentials } from '@shared/models/device.models';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { SelectWidgetTypeDialogComponent } from '@home/pages/widget/select-widget-type-dialog.component'; import { SelectWidgetTypeDialogComponent } from '@home/pages/widget/select-widget-type-dialog.component';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { ImportExportService } from '@home/components/import-export/import-export.service';
@Component({ @Component({
selector: 'tb-widget-library', selector: 'tb-widget-library',
@ -94,11 +95,12 @@ export class WidgetLibraryComponent extends PageComponent implements OnInit {
private router: Router, private router: Router,
private widgetService: WidgetService, private widgetService: WidgetService,
private dialogService: DialogService, private dialogService: DialogService,
private importExport: ImportExportService,
private dialog: MatDialog, private dialog: MatDialog,
private translate: TranslateService) { private translate: TranslateService) {
super(store); super(store);
this.authUser = getCurrentAuthUser(store); this.authUser = getCurrentAuthUser(this.store);
this.widgetsBundle = this.route.snapshot.data.widgetsBundle; this.widgetsBundle = this.route.snapshot.data.widgetsBundle;
this.widgetsData = this.route.snapshot.data.widgetsData; this.widgetsData = this.route.snapshot.data.widgetsData;
if (this.authUser.authority === Authority.TENANT_ADMIN) { if (this.authUser.authority === Authority.TENANT_ADMIN) {
@ -119,7 +121,23 @@ export class WidgetLibraryComponent extends PageComponent implements OnInit {
if ($event) { if ($event) {
$event.stopPropagation(); $event.stopPropagation();
} }
this.dialogService.todo(); this.importExport.importWidgetType(this.widgetsBundle.alias).subscribe(
(widgetTypeInstance) => {
if (widgetTypeInstance) {
this.reload();
}
}
);
}
private reload() {
const bundleAlias = this.widgetsBundle.alias;
const isSystem = this.widgetsBundle.tenantId.id === NULL_UUID;
this.widgetService.loadBundleLibraryWidgets(bundleAlias, isSystem).subscribe(
(widgets) => {
this.widgetsData = {widgets};
}
);
} }
openWidgetType($event: Event, widget?: Widget): void { openWidgetType($event: Event, widget?: Widget): void {
@ -147,14 +165,14 @@ export class WidgetLibraryComponent extends PageComponent implements OnInit {
if ($event) { if ($event) {
$event.stopPropagation(); $event.stopPropagation();
} }
this.dialogService.todo(); this.importExport.exportWidgetType(widget.typeId.id);
} }
removeWidgetType($event: Event, widget: Widget): Observable<boolean> { removeWidgetType($event: Event, widget: Widget): void {
if ($event) { if ($event) {
$event.stopPropagation(); $event.stopPropagation();
} }
return this.dialogService.confirm( this.dialogService.confirm(
this.translate.instant('widget.remove-widget-type-title', {widgetName: widget.config.title}), this.translate.instant('widget.remove-widget-type-title', {widgetName: widget.config.title}),
this.translate.instant('widget.remove-widget-type-text'), this.translate.instant('widget.remove-widget-type-text'),
this.translate.instant('action.no'), this.translate.instant('action.no'),
@ -169,13 +187,13 @@ export class WidgetLibraryComponent extends PageComponent implements OnInit {
}), }),
map((result) => { map((result) => {
if (result !== false) { if (result !== false) {
this.widgetsData.widgets.splice(this.widgetsData.widgets.indexOf(widget), 1); this.reload();
return true; return true;
} else { } else {
return false; return false;
} }
} }
)); )).subscribe();
} }
} }

17
ui-ngx/src/app/modules/home/pages/widget/widgets-bundles-table-config.resolver.ts

@ -36,6 +36,7 @@ import {AppState} from '@core/core.state';
import {getCurrentAuthUser} from '@app/core/auth/auth.selectors'; import {getCurrentAuthUser} from '@app/core/auth/auth.selectors';
import {Authority} from '@shared/models/authority.enum'; import {Authority} from '@shared/models/authority.enum';
import {DialogService} from '@core/services/dialog.service'; import {DialogService} from '@core/services/dialog.service';
import { ImportExportService } from '@home/components/import-export/import-export.service';
@Injectable() @Injectable()
export class WidgetsBundlesTableConfigResolver implements Resolve<EntityTableConfig<WidgetsBundle>> { export class WidgetsBundlesTableConfigResolver implements Resolve<EntityTableConfig<WidgetsBundle>> {
@ -46,6 +47,7 @@ export class WidgetsBundlesTableConfigResolver implements Resolve<EntityTableCon
private dialogService: DialogService, private dialogService: DialogService,
private widgetsService: WidgetService, private widgetsService: WidgetService,
private translate: TranslateService, private translate: TranslateService,
private importExport: ImportExportService,
private datePipe: DatePipe, private datePipe: DatePipe,
private router: Router) { private router: Router) {
@ -124,11 +126,13 @@ export class WidgetsBundlesTableConfigResolver implements Resolve<EntityTableCon
} }
importWidgetsBundle($event: Event) { importWidgetsBundle($event: Event) {
if ($event) { this.importExport.importWidgetsBundle().subscribe(
$event.stopPropagation(); (widgetsBundle) => {
} if (widgetsBundle) {
// TODO: this.config.table.updateData();
this.dialogService.todo(); }
}
);
} }
openWidgetsBundle($event: Event, widgetsBundle: WidgetsBundle) { openWidgetsBundle($event: Event, widgetsBundle: WidgetsBundle) {
@ -142,8 +146,7 @@ export class WidgetsBundlesTableConfigResolver implements Resolve<EntityTableCon
if ($event) { if ($event) {
$event.stopPropagation(); $event.stopPropagation();
} }
// TODO: this.importExport.exportWidgetsBundle(widgetsBundle.id.id);
this.dialogService.todo();
} }
onWidgetsBundleAction(action: EntityAction<WidgetsBundle>): boolean { onWidgetsBundleAction(action: EntityAction<WidgetsBundle>): boolean {

4
ui-ngx/src/scss/mixins.scss

@ -21,12 +21,12 @@
min-height: #{$size}px; min-height: #{$size}px;
font-size: #{$size}px; font-size: #{$size}px;
line-height: #{$size}px; line-height: #{$size}px;
svg { /* svg {
width: 24px; width: 24px;
height: 24px; height: 24px;
transform: scale($size/24); transform: scale($size/24);
transform-origin: top; transform-origin: top;
} }*/
} }
@mixin tb-mat-icon-button-size($size) { @mixin tb-mat-icon-button-size($size) {

Loading…
Cancel
Save