Commit 65b7c139cf31cfc6dd78af08ca509924d00d5579

Authored by Igor Kulikov
1 parent 8ee3f0bf

Widgets library import export support.

... ... @@ -55,7 +55,7 @@
55 55 ],
56 56 "templateHtml": "<canvas id=\"pieChart\"></canvas>\n",
57 57 "templateCss": "",
58   - "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",
  58 + "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",
59 59 "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}",
60 60 "dataKeySettingsSchema": "{}\n",
61 61 "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}}"
... ...
... ... @@ -21,7 +21,7 @@ import { HttpClient } from '@angular/common/http';
21 21 import { PageLink } from '@shared/models/page/page-link';
22 22 import { PageData } from '@shared/models/page/page-data';
23 23 import { WidgetsBundle } from '@shared/models/widgets-bundle.model';
24   -import { WidgetType, widgetType, WidgetTypeData, widgetTypesData } from '@shared/models/widget.models';
  24 +import { Widget, WidgetType, widgetType, WidgetTypeData, widgetTypesData } from '@shared/models/widget.models';
25 25 import { UtilsService } from '@core/services/utils.service';
26 26 import { TranslateService } from '@ngx-translate/core';
27 27 import { ResourcesService } from '../services/resources.service';
... ... @@ -119,6 +119,59 @@ export class WidgetService {
119 119 defaultHttpOptions(ignoreLoading, ignoreErrors));
120 120 }
121 121
  122 + public loadBundleLibraryWidgets(bundleAlias: string, isSystem: boolean,
  123 + ignoreErrors: boolean = false, ignoreLoading: boolean = false): Observable<Array<Widget>> {
  124 + return this.getBundleWidgetTypes(bundleAlias, isSystem, ignoreErrors, ignoreLoading).pipe(
  125 + map((types) => {
  126 + types = types.sort((a, b) => {
  127 + let result = widgetType[b.descriptor.type].localeCompare(widgetType[a.descriptor.type]);
  128 + if (result === 0) {
  129 + result = b.createdTime - a.createdTime;
  130 + }
  131 + return result;
  132 + });
  133 + const widgetTypes = new Array<Widget>();
  134 + let top = 0;
  135 + const lastTop = [0, 0, 0];
  136 + let col = 0;
  137 + let column = 0;
  138 + types.forEach((type) => {
  139 + const widgetTypeInfo = toWidgetInfo(type);
  140 + const sizeX = 8;
  141 + const sizeY = Math.floor(widgetTypeInfo.sizeY);
  142 + const widget: Widget = {
  143 + typeId: type.id,
  144 + isSystemType: isSystem,
  145 + bundleAlias,
  146 + typeAlias: widgetTypeInfo.alias,
  147 + type: widgetTypeInfo.type,
  148 + title: widgetTypeInfo.widgetName,
  149 + sizeX,
  150 + sizeY,
  151 + row: top,
  152 + col,
  153 + config: JSON.parse(widgetTypeInfo.defaultConfig)
  154 + };
  155 +
  156 + widget.config.title = widgetTypeInfo.widgetName;
  157 +
  158 + widgetTypes.push(widget);
  159 + top += sizeY;
  160 + if (top > lastTop[column] + 10) {
  161 + lastTop[column] = top;
  162 + column++;
  163 + if (column > 2) {
  164 + column = 0;
  165 + }
  166 + top = lastTop[column];
  167 + col = column * 8;
  168 + }
  169 + });
  170 + return widgetTypes;
  171 + })
  172 + );
  173 + }
  174 +
122 175 public getWidgetType(bundleAlias: string, widgetTypeAlias: string, isSystem: boolean,
123 176 ignoreErrors: boolean = false, ignoreLoading: boolean = false): Observable<WidgetType> {
124 177 return this.http.get<WidgetType>(`/api/widgetType?isSystem=${isSystem}&bundleAlias=${bundleAlias}&alias=${widgetTypeAlias}`,
... ...
... ... @@ -14,10 +14,16 @@
14 14 /// limitations under the License.
15 15 ///
16 16
17   -import { Widget } from '@app/shared/models/widget.models';
  17 +import { Widget, WidgetType } from '@app/shared/models/widget.models';
18 18 import { DashboardLayoutId } from '@shared/models/dashboard.models';
  19 +import { WidgetsBundle } from '@shared/models/widgets-bundle.model';
19 20
20 21 export interface ImportWidgetResult {
21 22 widget: Widget;
22 23 layoutId: DashboardLayoutId;
23 24 }
  25 +
  26 +export interface WidgetsBundleItem {
  27 + widgetsBundle: WidgetsBundle;
  28 + widgetTypes: WidgetType[];
  29 +}
... ...
... ... @@ -38,15 +38,18 @@ import { forkJoin, Observable, of } from 'rxjs';
38 38 import { catchError, map, mergeMap } from 'rxjs/operators';
39 39 import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
40 40 import { EntityService } from '@core/http/entity.service';
41   -import { Widget, WidgetSize } from '@shared/models/widget.models';
  41 +import { Widget, WidgetSize, WidgetType } from '@shared/models/widget.models';
42 42 import {
43 43 EntityAliasesDialogComponent,
44 44 EntityAliasesDialogData
45 45 } from '@home/components/alias/entity-aliases-dialog.component';
46 46 import { ItemBufferService, WidgetItem } from '@core/services/item-buffer.service';
47   -import { ImportWidgetResult } from './import-export.models';
  47 +import { ImportWidgetResult, WidgetsBundleItem } from './import-export.models';
48 48 import { EntityType } from '@shared/models/entity-type.models';
49 49 import { UtilsService } from '@core/services/utils.service';
  50 +import { WidgetService } from '@core/http/widget.service';
  51 +import { NULL_UUID } from '@shared/models/id/has-uuid';
  52 +import { WidgetsBundle } from '@shared/models/widgets-bundle.model';
50 53
51 54 @Injectable()
52 55 export class ImportExportService {
... ... @@ -57,6 +60,7 @@ export class ImportExportService {
57 60 private translate: TranslateService,
58 61 private dashboardService: DashboardService,
59 62 private dashboardUtils: DashboardUtilsService,
  63 + private widgetService: WidgetService,
60 64 private entityService: EntityService,
61 65 private utils: UtilsService,
62 66 private itembuffer: ItemBufferService,
... ... @@ -72,13 +76,7 @@ export class ImportExportService {
72 76 this.exportToPc(this.prepareDashboardExport(dashboard), name + '.json');
73 77 },
74 78 (e) => {
75   - let message = e;
76   - if (!message) {
77   - message = this.translate.instant('error.unknown-error');
78   - }
79   - this.store.dispatch(new ActionNotificationShow(
80   - {message: this.translate.instant('dashboard.export-failed-error', {error: message}),
81   - type: 'error'}));
  79 + this.handleExportError(e, 'dashboard.export-failed-error');
82 80 }
83 81 );
84 82 }
... ... @@ -223,6 +221,119 @@ export class ImportExportService {
223 221 );
224 222 }
225 223
  224 + public exportWidgetType(widgetTypeId: string) {
  225 + this.widgetService.getWidgetTypeById(widgetTypeId).subscribe(
  226 + (widgetType) => {
  227 + if (isDefined(widgetType.bundleAlias)) {
  228 + delete widgetType.bundleAlias;
  229 + }
  230 + let name = widgetType.name;
  231 + name = name.toLowerCase().replace(/\W/g, '_');
  232 + this.exportToPc(this.prepareExport(widgetType), name + '.json');
  233 + },
  234 + (e) => {
  235 + this.handleExportError(e, 'widget-type.export-failed-error');
  236 + }
  237 + );
  238 + }
  239 +
  240 + public importWidgetType(bundleAlias: string): Observable<WidgetType> {
  241 + return this.openImportDialog('widget-type.import', 'widget-type.widget-type-file').pipe(
  242 + mergeMap((widgetType: WidgetType) => {
  243 + if (!this.validateImportedWidgetType(widgetType)) {
  244 + this.store.dispatch(new ActionNotificationShow(
  245 + {message: this.translate.instant('widget-type.invalid-widget-type-file-error'),
  246 + type: 'error'}));
  247 + throw new Error('Invalid widget type file');
  248 + } else {
  249 + widgetType.bundleAlias = bundleAlias;
  250 + return this.widgetService.saveImportedWidgetType(widgetType);
  251 + }
  252 + }),
  253 + catchError((err) => {
  254 + return of(null);
  255 + })
  256 + );
  257 + }
  258 +
  259 + public exportWidgetsBundle(widgetsBundleId: string) {
  260 + this.widgetService.getWidgetsBundle(widgetsBundleId).subscribe(
  261 + (widgetsBundle) => {
  262 + const bundleAlias = widgetsBundle.alias;
  263 + const isSystem = widgetsBundle.tenantId.id === NULL_UUID;
  264 + this.widgetService.getBundleWidgetTypes(bundleAlias, isSystem).subscribe(
  265 + (widgetTypes) => {
  266 + const widgetsBundleItem: WidgetsBundleItem = {
  267 + widgetsBundle: this.prepareExport(widgetsBundle),
  268 + widgetTypes: []
  269 + };
  270 + for (const widgetType of widgetTypes) {
  271 + if (isDefined(widgetType.bundleAlias)) {
  272 + delete widgetType.bundleAlias;
  273 + }
  274 + widgetsBundleItem.widgetTypes.push(this.prepareExport(widgetType));
  275 + }
  276 + let name = widgetsBundle.title;
  277 + name = name.toLowerCase().replace(/\W/g, '_');
  278 + this.exportToPc(widgetsBundleItem, name + '.json');
  279 + },
  280 + (e) => {
  281 + this.handleExportError(e, 'widgets-bundle.export-failed-error');
  282 + }
  283 + );
  284 + },
  285 + (e) => {
  286 + this.handleExportError(e, 'widgets-bundle.export-failed-error');
  287 + }
  288 + );
  289 + }
  290 +
  291 + public importWidgetsBundle(): Observable<WidgetsBundle> {
  292 + return this.openImportDialog('widgets-bundle.import', 'widgets-bundle.widgets-bundle-file').pipe(
  293 + mergeMap((widgetsBundleItem: WidgetsBundleItem) => {
  294 + if (!this.validateImportedWidgetsBundle(widgetsBundleItem)) {
  295 + this.store.dispatch(new ActionNotificationShow(
  296 + {message: this.translate.instant('widgets-bundle.invalid-widgets-bundle-file-error'),
  297 + type: 'error'}));
  298 + throw new Error('Invalid widgets bundle file');
  299 + } else {
  300 + const widgetsBundle = widgetsBundleItem.widgetsBundle;
  301 + return this.widgetService.saveWidgetsBundle(widgetsBundle).pipe(
  302 + mergeMap((savedWidgetsBundle) => {
  303 + const bundleAlias = savedWidgetsBundle.alias;
  304 + const widgetTypes = widgetsBundleItem.widgetTypes;
  305 + if (widgetTypes.length) {
  306 + const saveWidgetTypesObservables: Array<Observable<WidgetType>> = [];
  307 + for (const widgetType of widgetTypes) {
  308 + widgetType.bundleAlias = bundleAlias;
  309 + saveWidgetTypesObservables.push(this.widgetService.saveImportedWidgetType(widgetType));
  310 + }
  311 + return forkJoin(saveWidgetTypesObservables).pipe(
  312 + map(() => savedWidgetsBundle)
  313 + );
  314 + } else {
  315 + return of(savedWidgetsBundle);
  316 + }
  317 + }
  318 + ));
  319 + }
  320 + }),
  321 + catchError((err) => {
  322 + return of(null);
  323 + })
  324 + );
  325 + }
  326 +
  327 + private handleExportError(e: any, errorDetailsMessageId: string) {
  328 + let message = e;
  329 + if (!message) {
  330 + message = this.translate.instant('error.unknown-error');
  331 + }
  332 + this.store.dispatch(new ActionNotificationShow(
  333 + {message: this.translate.instant(errorDetailsMessageId, {error: message}),
  334 + type: 'error'}));
  335 + }
  336 +
226 337 private validateImportedDashboard(dashboard: Dashboard): boolean {
227 338 if (isUndefined(dashboard.title) || isUndefined(dashboard.configuration)) {
228 339 return false;
... ... @@ -246,6 +357,34 @@ export class ImportExportService {
246 357 return true;
247 358 }
248 359
  360 + private validateImportedWidgetType(widgetType: WidgetType): boolean {
  361 + if (isUndefined(widgetType.name)
  362 + || isUndefined(widgetType.descriptor)) {
  363 + return false;
  364 + }
  365 + return true;
  366 + }
  367 +
  368 + private validateImportedWidgetsBundle(widgetsBundleItem: WidgetsBundleItem): boolean {
  369 + if (isUndefined(widgetsBundleItem.widgetsBundle)) {
  370 + return false;
  371 + }
  372 + if (isUndefined(widgetsBundleItem.widgetTypes)) {
  373 + return false;
  374 + }
  375 + const widgetsBundle = widgetsBundleItem.widgetsBundle;
  376 + if (isUndefined(widgetsBundle.title)) {
  377 + return false;
  378 + }
  379 + const widgetTypes = widgetsBundleItem.widgetTypes;
  380 + for (const widgetType of widgetTypes) {
  381 + if (!this.validateImportedWidgetType(widgetType)) {
  382 + return false;
  383 + }
  384 + }
  385 + return true;
  386 + }
  387 +
249 388 private saveImportedDashboard(dashboard: Dashboard): Observable<Dashboard> {
250 389 return this.dashboardService.saveDashboard(dashboard);
251 390 }
... ...
... ... @@ -114,7 +114,7 @@ export class DashboardWidgets implements Iterable<DashboardWidget> {
114 114 updateRecords.push({
115 115 widget: added.item,
116 116 widgetId: added.item.id,
117   - widgetLayout: this.widgetLayouts[added.item.id],
  117 + widgetLayout: this.widgetLayouts ? this.widgetLayouts[added.item.id] : null,
118 118 operation: 'add'
119 119 });
120 120 });
... ...
... ... @@ -63,54 +63,10 @@ export class WidgetsTypesDataResolver implements Resolve<WidgetsData> {
63 63 const widgetsBundle: WidgetsBundle = route.parent.data.widgetsBundle;
64 64 const bundleAlias = widgetsBundle.alias;
65 65 const isSystem = widgetsBundle.tenantId.id === NULL_UUID;
66   - return this.widgetsService.getBundleWidgetTypes(bundleAlias,
  66 + return this.widgetsService.loadBundleLibraryWidgets(bundleAlias,
67 67 isSystem).pipe(
68   - map((types) => {
69   - types = types.sort((a, b) => {
70   - let result = widgetType[b.descriptor.type].localeCompare(widgetType[a.descriptor.type]);
71   - if (result === 0) {
72   - result = b.createdTime - a.createdTime;
73   - }
74   - return result;
75   - });
76   - const widgetTypes = new Array<Widget>();
77   - let top = 0;
78   - const lastTop = [0, 0, 0];
79   - let col = 0;
80   - let column = 0;
81   - types.forEach((type) => {
82   - const widgetTypeInfo = toWidgetInfo(type);
83   - const sizeX = 8;
84   - const sizeY = Math.floor(widgetTypeInfo.sizeY);
85   - const widget: Widget = {
86   - typeId: type.id,
87   - isSystemType: isSystem,
88   - bundleAlias,
89   - typeAlias: widgetTypeInfo.alias,
90   - type: widgetTypeInfo.type,
91   - title: widgetTypeInfo.widgetName,
92   - sizeX,
93   - sizeY,
94   - row: top,
95   - col,
96   - config: JSON.parse(widgetTypeInfo.defaultConfig)
97   - };
98   -
99   - widget.config.title = widgetTypeInfo.widgetName;
100   -
101   - widgetTypes.push(widget);
102   - top += sizeY;
103   - if (top > lastTop[column] + 10) {
104   - lastTop[column] = top;
105   - column++;
106   - if (column > 2) {
107   - column = 0;
108   - }
109   - top = lastTop[column];
110   - col = column * 8;
111   - }
112   - });
113   - return { widgets: widgetTypes };
  68 + map((widgets) => {
  69 + return { widgets };
114 70 }
115 71 ));
116 72 }
... ...
... ... @@ -14,7 +14,7 @@
14 14 /// limitations under the License.
15 15 ///
16 16
17   -import { Component, OnInit, ViewChild } from '@angular/core';
  17 +import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
18 18 import { Store } from '@ngrx/store';
19 19 import { AppState } from '@core/core.state';
20 20 import { PageComponent } from '@shared/components/page.component';
... ... @@ -24,7 +24,7 @@ import { WidgetsBundle } from '@shared/models/widgets-bundle.model';
24 24 import { ActivatedRoute, Router } from '@angular/router';
25 25 import { Authority } from '@shared/models/authority.enum';
26 26 import { NULL_UUID } from '@shared/models/id/has-uuid';
27   -import { Observable, of } from 'rxjs';
  27 +import { Observable, of, Subscription } from 'rxjs';
28 28 import { Widget, widgetType } from '@app/shared/models/widget.models';
29 29 import { WidgetService } from '@core/http/widget.service';
30 30 import { map, mergeMap, share } from 'rxjs/operators';
... ... @@ -42,6 +42,7 @@ import { DeviceCredentials } from '@shared/models/device.models';
42 42 import { MatDialog } from '@angular/material/dialog';
43 43 import { SelectWidgetTypeDialogComponent } from '@home/pages/widget/select-widget-type-dialog.component';
44 44 import { TranslateService } from '@ngx-translate/core';
  45 +import { ImportExportService } from '@home/components/import-export/import-export.service';
45 46
46 47 @Component({
47 48 selector: 'tb-widget-library',
... ... @@ -94,11 +95,12 @@ export class WidgetLibraryComponent extends PageComponent implements OnInit {
94 95 private router: Router,
95 96 private widgetService: WidgetService,
96 97 private dialogService: DialogService,
  98 + private importExport: ImportExportService,
97 99 private dialog: MatDialog,
98 100 private translate: TranslateService) {
99 101 super(store);
100 102
101   - this.authUser = getCurrentAuthUser(store);
  103 + this.authUser = getCurrentAuthUser(this.store);
102 104 this.widgetsBundle = this.route.snapshot.data.widgetsBundle;
103 105 this.widgetsData = this.route.snapshot.data.widgetsData;
104 106 if (this.authUser.authority === Authority.TENANT_ADMIN) {
... ... @@ -119,7 +121,23 @@ export class WidgetLibraryComponent extends PageComponent implements OnInit {
119 121 if ($event) {
120 122 $event.stopPropagation();
121 123 }
122   - this.dialogService.todo();
  124 + this.importExport.importWidgetType(this.widgetsBundle.alias).subscribe(
  125 + (widgetTypeInstance) => {
  126 + if (widgetTypeInstance) {
  127 + this.reload();
  128 + }
  129 + }
  130 + );
  131 + }
  132 +
  133 + private reload() {
  134 + const bundleAlias = this.widgetsBundle.alias;
  135 + const isSystem = this.widgetsBundle.tenantId.id === NULL_UUID;
  136 + this.widgetService.loadBundleLibraryWidgets(bundleAlias, isSystem).subscribe(
  137 + (widgets) => {
  138 + this.widgetsData = {widgets};
  139 + }
  140 + );
123 141 }
124 142
125 143 openWidgetType($event: Event, widget?: Widget): void {
... ... @@ -147,14 +165,14 @@ export class WidgetLibraryComponent extends PageComponent implements OnInit {
147 165 if ($event) {
148 166 $event.stopPropagation();
149 167 }
150   - this.dialogService.todo();
  168 + this.importExport.exportWidgetType(widget.typeId.id);
151 169 }
152 170
153   - removeWidgetType($event: Event, widget: Widget): Observable<boolean> {
  171 + removeWidgetType($event: Event, widget: Widget): void {
154 172 if ($event) {
155 173 $event.stopPropagation();
156 174 }
157   - return this.dialogService.confirm(
  175 + this.dialogService.confirm(
158 176 this.translate.instant('widget.remove-widget-type-title', {widgetName: widget.config.title}),
159 177 this.translate.instant('widget.remove-widget-type-text'),
160 178 this.translate.instant('action.no'),
... ... @@ -169,13 +187,13 @@ export class WidgetLibraryComponent extends PageComponent implements OnInit {
169 187 }),
170 188 map((result) => {
171 189 if (result !== false) {
172   - this.widgetsData.widgets.splice(this.widgetsData.widgets.indexOf(widget), 1);
  190 + this.reload();
173 191 return true;
174 192 } else {
175 193 return false;
176 194 }
177 195 }
178   - ));
  196 + )).subscribe();
179 197 }
180 198
181 199 }
... ...
... ... @@ -36,6 +36,7 @@ import {AppState} from '@core/core.state';
36 36 import {getCurrentAuthUser} from '@app/core/auth/auth.selectors';
37 37 import {Authority} from '@shared/models/authority.enum';
38 38 import {DialogService} from '@core/services/dialog.service';
  39 +import { ImportExportService } from '@home/components/import-export/import-export.service';
39 40
40 41 @Injectable()
41 42 export class WidgetsBundlesTableConfigResolver implements Resolve<EntityTableConfig<WidgetsBundle>> {
... ... @@ -46,6 +47,7 @@ export class WidgetsBundlesTableConfigResolver implements Resolve<EntityTableCon
46 47 private dialogService: DialogService,
47 48 private widgetsService: WidgetService,
48 49 private translate: TranslateService,
  50 + private importExport: ImportExportService,
49 51 private datePipe: DatePipe,
50 52 private router: Router) {
51 53
... ... @@ -124,11 +126,13 @@ export class WidgetsBundlesTableConfigResolver implements Resolve<EntityTableCon
124 126 }
125 127
126 128 importWidgetsBundle($event: Event) {
127   - if ($event) {
128   - $event.stopPropagation();
129   - }
130   - // TODO:
131   - this.dialogService.todo();
  129 + this.importExport.importWidgetsBundle().subscribe(
  130 + (widgetsBundle) => {
  131 + if (widgetsBundle) {
  132 + this.config.table.updateData();
  133 + }
  134 + }
  135 + );
132 136 }
133 137
134 138 openWidgetsBundle($event: Event, widgetsBundle: WidgetsBundle) {
... ... @@ -142,8 +146,7 @@ export class WidgetsBundlesTableConfigResolver implements Resolve<EntityTableCon
142 146 if ($event) {
143 147 $event.stopPropagation();
144 148 }
145   - // TODO:
146   - this.dialogService.todo();
  149 + this.importExport.exportWidgetsBundle(widgetsBundle.id.id);
147 150 }
148 151
149 152 onWidgetsBundleAction(action: EntityAction<WidgetsBundle>): boolean {
... ...
... ... @@ -21,12 +21,12 @@
21 21 min-height: #{$size}px;
22 22 font-size: #{$size}px;
23 23 line-height: #{$size}px;
24   - svg {
  24 +/* svg {
25 25 width: 24px;
26 26 height: 24px;
27 27 transform: scale($size/24);
28 28 transform-origin: top;
29   - }
  29 + }*/
30 30 }
31 31
32 32 @mixin tb-mat-icon-button-size($size) {
... ...