Commit 65b7c139cf31cfc6dd78af08ca509924d00d5579
1 parent
8ee3f0bf
Widgets library import export support.
Showing
9 changed files
with
253 additions
and
78 deletions
... | ... | @@ -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) { | ... | ... |