Commit 22a1d24291d8e0a794f45935666f1c6fe3710676
1 parent
4c87b36a
Added to widgets bundles and widget functional preview and description
Showing
52 changed files
with
398 additions
and
159 deletions
1 | +-- | ||
2 | +-- Copyright © 2016-2021 The Thingsboard Authors | ||
3 | +-- | ||
4 | +-- Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +-- you may not use this file except in compliance with the License. | ||
6 | +-- You may obtain a copy of the License at | ||
7 | +-- | ||
8 | +-- http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +-- | ||
10 | +-- Unless required by applicable law or agreed to in writing, software | ||
11 | +-- distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +-- See the License for the specific language governing permissions and | ||
14 | +-- limitations under the License. | ||
15 | +-- | ||
16 | + | ||
17 | +ALTER TABLE widget_type | ||
18 | + ADD COLUMN IF NOT EXISTS image varchar (1000000), | ||
19 | + ADD COLUMN IF NOT EXISTS description varchar (255); | ||
20 | + | ||
21 | +ALTER TABLE widgets_bundle | ||
22 | + ADD COLUMN IF NOT EXISTS image varchar (1000000), | ||
23 | + ADD COLUMN IF NOT EXISTS description varchar (255); |
@@ -187,6 +187,7 @@ public class ThingsboardInstallService { | @@ -187,6 +187,7 @@ public class ThingsboardInstallService { | ||
187 | databaseEntitiesUpgradeService.upgradeDatabase("3.2.0"); | 187 | databaseEntitiesUpgradeService.upgradeDatabase("3.2.0"); |
188 | case "3.2.1": | 188 | case "3.2.1": |
189 | log.info("Upgrading ThingsBoard from version 3.2.1 to 3.3.0 ..."); | 189 | log.info("Upgrading ThingsBoard from version 3.2.1 to 3.3.0 ..."); |
190 | + databaseEntitiesUpgradeService.upgradeDatabase("3.2.1"); | ||
190 | log.info("Updating system data..."); | 191 | log.info("Updating system data..."); |
191 | systemDataLoaderService.updateSystemWidgets(); | 192 | systemDataLoaderService.updateSystemWidgets(); |
192 | break; | 193 | break; |
@@ -434,6 +434,17 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService | @@ -434,6 +434,17 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService | ||
434 | log.info("Schema updated."); | 434 | log.info("Schema updated."); |
435 | } | 435 | } |
436 | break; | 436 | break; |
437 | + case "3.2.1": | ||
438 | + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { | ||
439 | + log.info("Updating schema ..."); | ||
440 | + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.2.1", SCHEMA_UPDATE_SQL); | ||
441 | + loadSql(schemaUpdateFile, conn); | ||
442 | + conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3003000;"); | ||
443 | + log.info("Schema updated."); | ||
444 | + } catch (Exception e) { | ||
445 | + log.error("Failed updating schema!!!", e); | ||
446 | + } | ||
447 | + break; | ||
437 | default: | 448 | default: |
438 | throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); | 449 | throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); |
439 | } | 450 | } |
@@ -31,6 +31,8 @@ public class WidgetType extends BaseData<WidgetTypeId> implements HasTenantId { | @@ -31,6 +31,8 @@ public class WidgetType extends BaseData<WidgetTypeId> implements HasTenantId { | ||
31 | private String bundleAlias; | 31 | private String bundleAlias; |
32 | private String alias; | 32 | private String alias; |
33 | private String name; | 33 | private String name; |
34 | + private String image; | ||
35 | + private String description; | ||
34 | private transient JsonNode descriptor; | 36 | private transient JsonNode descriptor; |
35 | 37 | ||
36 | public WidgetType() { | 38 | public WidgetType() { |
@@ -47,6 +49,8 @@ public class WidgetType extends BaseData<WidgetTypeId> implements HasTenantId { | @@ -47,6 +49,8 @@ public class WidgetType extends BaseData<WidgetTypeId> implements HasTenantId { | ||
47 | this.bundleAlias = widgetType.getBundleAlias(); | 49 | this.bundleAlias = widgetType.getBundleAlias(); |
48 | this.alias = widgetType.getAlias(); | 50 | this.alias = widgetType.getAlias(); |
49 | this.name = widgetType.getName(); | 51 | this.name = widgetType.getName(); |
52 | + this.image = widgetType.getImage(); | ||
53 | + this.description = widgetType.getDescription(); | ||
50 | this.descriptor = widgetType.getDescriptor(); | 54 | this.descriptor = widgetType.getDescriptor(); |
51 | } | 55 | } |
52 | 56 | ||
@@ -82,6 +86,14 @@ public class WidgetType extends BaseData<WidgetTypeId> implements HasTenantId { | @@ -82,6 +86,14 @@ public class WidgetType extends BaseData<WidgetTypeId> implements HasTenantId { | ||
82 | this.name = name; | 86 | this.name = name; |
83 | } | 87 | } |
84 | 88 | ||
89 | + public String getImage() { return image; } | ||
90 | + | ||
91 | + public void setImage(String image) { this.image = image; } | ||
92 | + | ||
93 | + public String getDescription() { return description; } | ||
94 | + | ||
95 | + public void setDescription(String description) { this.description = description; } | ||
96 | + | ||
85 | public JsonNode getDescriptor() { | 97 | public JsonNode getDescriptor() { |
86 | return descriptor; | 98 | return descriptor; |
87 | } | 99 | } |
@@ -97,9 +109,10 @@ public class WidgetType extends BaseData<WidgetTypeId> implements HasTenantId { | @@ -97,9 +109,10 @@ public class WidgetType extends BaseData<WidgetTypeId> implements HasTenantId { | ||
97 | sb.append(", bundleAlias='").append(bundleAlias).append('\''); | 109 | sb.append(", bundleAlias='").append(bundleAlias).append('\''); |
98 | sb.append(", alias='").append(alias).append('\''); | 110 | sb.append(", alias='").append(alias).append('\''); |
99 | sb.append(", name='").append(name).append('\''); | 111 | sb.append(", name='").append(name).append('\''); |
112 | + sb.append(", image='").append(image).append('\''); | ||
113 | + sb.append(", description='").append(description).append('\''); | ||
100 | sb.append(", descriptor=").append(descriptor); | 114 | sb.append(", descriptor=").append(descriptor); |
101 | sb.append('}'); | 115 | sb.append('}'); |
102 | return sb.toString(); | 116 | return sb.toString(); |
103 | } | 117 | } |
104 | - | ||
105 | } | 118 | } |
@@ -29,7 +29,8 @@ public class WidgetsBundle extends SearchTextBased<WidgetsBundleId> implements H | @@ -29,7 +29,8 @@ public class WidgetsBundle extends SearchTextBased<WidgetsBundleId> implements H | ||
29 | private TenantId tenantId; | 29 | private TenantId tenantId; |
30 | private String alias; | 30 | private String alias; |
31 | private String title; | 31 | private String title; |
32 | - private byte[] image; | 32 | + private String image; |
33 | + private String description; | ||
33 | 34 | ||
34 | public WidgetsBundle() { | 35 | public WidgetsBundle() { |
35 | super(); | 36 | super(); |
@@ -45,6 +46,7 @@ public class WidgetsBundle extends SearchTextBased<WidgetsBundleId> implements H | @@ -45,6 +46,7 @@ public class WidgetsBundle extends SearchTextBased<WidgetsBundleId> implements H | ||
45 | this.alias = widgetsBundle.getAlias(); | 46 | this.alias = widgetsBundle.getAlias(); |
46 | this.title = widgetsBundle.getTitle(); | 47 | this.title = widgetsBundle.getTitle(); |
47 | this.image = widgetsBundle.getImage(); | 48 | this.image = widgetsBundle.getImage(); |
49 | + this.description = widgetsBundle.getDescription(); | ||
48 | } | 50 | } |
49 | 51 | ||
50 | public TenantId getTenantId() { | 52 | public TenantId getTenantId() { |
@@ -71,14 +73,18 @@ public class WidgetsBundle extends SearchTextBased<WidgetsBundleId> implements H | @@ -71,14 +73,18 @@ public class WidgetsBundle extends SearchTextBased<WidgetsBundleId> implements H | ||
71 | this.title = title; | 73 | this.title = title; |
72 | } | 74 | } |
73 | 75 | ||
74 | - public byte[] getImage() { | 76 | + public String getImage() { |
75 | return image; | 77 | return image; |
76 | } | 78 | } |
77 | 79 | ||
78 | - public void setImage(byte[] image) { | 80 | + public void setImage(String image) { |
79 | this.image = image; | 81 | this.image = image; |
80 | } | 82 | } |
81 | 83 | ||
84 | + public String getDescription() { return description; } | ||
85 | + | ||
86 | + public void setDescription(String description) { this.description = description; } | ||
87 | + | ||
82 | @Override | 88 | @Override |
83 | public String getSearchText() { | 89 | public String getSearchText() { |
84 | return getTitle(); | 90 | return getTitle(); |
@@ -90,7 +96,8 @@ public class WidgetsBundle extends SearchTextBased<WidgetsBundleId> implements H | @@ -90,7 +96,8 @@ public class WidgetsBundle extends SearchTextBased<WidgetsBundleId> implements H | ||
90 | result = 31 * result + (tenantId != null ? tenantId.hashCode() : 0); | 96 | result = 31 * result + (tenantId != null ? tenantId.hashCode() : 0); |
91 | result = 31 * result + (alias != null ? alias.hashCode() : 0); | 97 | result = 31 * result + (alias != null ? alias.hashCode() : 0); |
92 | result = 31 * result + (title != null ? title.hashCode() : 0); | 98 | result = 31 * result + (title != null ? title.hashCode() : 0); |
93 | - result = 31 * result + Arrays.hashCode(image); | 99 | + result = 31 * result + (image != null ? image.hashCode() : 0); |
100 | + result = 31 * result + (description != null ? description.hashCode() : 0); | ||
94 | return result; | 101 | return result; |
95 | } | 102 | } |
96 | 103 | ||
@@ -105,7 +112,9 @@ public class WidgetsBundle extends SearchTextBased<WidgetsBundleId> implements H | @@ -105,7 +112,9 @@ public class WidgetsBundle extends SearchTextBased<WidgetsBundleId> implements H | ||
105 | if (tenantId != null ? !tenantId.equals(that.tenantId) : that.tenantId != null) return false; | 112 | if (tenantId != null ? !tenantId.equals(that.tenantId) : that.tenantId != null) return false; |
106 | if (alias != null ? !alias.equals(that.alias) : that.alias != null) return false; | 113 | if (alias != null ? !alias.equals(that.alias) : that.alias != null) return false; |
107 | if (title != null ? !title.equals(that.title) : that.title != null) return false; | 114 | if (title != null ? !title.equals(that.title) : that.title != null) return false; |
108 | - return Arrays.equals(image, that.image); | 115 | + if (image != null ? !image.equals(that.image) : that.image != null) return false; |
116 | + if (description != null ? !description.equals(that.image) : that.description != null) return false; | ||
117 | + return true; | ||
109 | } | 118 | } |
110 | 119 | ||
111 | @Override | 120 | @Override |
@@ -114,7 +123,8 @@ public class WidgetsBundle extends SearchTextBased<WidgetsBundleId> implements H | @@ -114,7 +123,8 @@ public class WidgetsBundle extends SearchTextBased<WidgetsBundleId> implements H | ||
114 | sb.append("tenantId=").append(tenantId); | 123 | sb.append("tenantId=").append(tenantId); |
115 | sb.append(", alias='").append(alias).append('\''); | 124 | sb.append(", alias='").append(alias).append('\''); |
116 | sb.append(", title='").append(title).append('\''); | 125 | sb.append(", title='").append(title).append('\''); |
117 | - sb.append(", image=").append(Arrays.toString(image)); | 126 | + sb.append(", image='").append(image).append('\''); |
127 | + sb.append(", description='").append(description).append('\''); | ||
118 | sb.append('}'); | 128 | sb.append('}'); |
119 | return sb.toString(); | 129 | return sb.toString(); |
120 | } | 130 | } |
@@ -304,6 +304,7 @@ public class ModelConstants { | @@ -304,6 +304,7 @@ public class ModelConstants { | ||
304 | public static final String WIDGETS_BUNDLE_ALIAS_PROPERTY = ALIAS_PROPERTY; | 304 | public static final String WIDGETS_BUNDLE_ALIAS_PROPERTY = ALIAS_PROPERTY; |
305 | public static final String WIDGETS_BUNDLE_TITLE_PROPERTY = TITLE_PROPERTY; | 305 | public static final String WIDGETS_BUNDLE_TITLE_PROPERTY = TITLE_PROPERTY; |
306 | public static final String WIDGETS_BUNDLE_IMAGE_PROPERTY = "image"; | 306 | public static final String WIDGETS_BUNDLE_IMAGE_PROPERTY = "image"; |
307 | + public static final String WIDGETS_BUNDLE_DESCRIPTION = "description"; | ||
307 | 308 | ||
308 | public static final String WIDGETS_BUNDLE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "widgets_bundle_by_tenant_and_search_text"; | 309 | public static final String WIDGETS_BUNDLE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "widgets_bundle_by_tenant_and_search_text"; |
309 | public static final String WIDGETS_BUNDLE_BY_TENANT_AND_ALIAS_COLUMN_FAMILY_NAME = "widgets_bundle_by_tenant_and_alias"; | 310 | public static final String WIDGETS_BUNDLE_BY_TENANT_AND_ALIAS_COLUMN_FAMILY_NAME = "widgets_bundle_by_tenant_and_alias"; |
@@ -316,6 +317,8 @@ public class ModelConstants { | @@ -316,6 +317,8 @@ public class ModelConstants { | ||
316 | public static final String WIDGET_TYPE_BUNDLE_ALIAS_PROPERTY = "bundle_alias"; | 317 | public static final String WIDGET_TYPE_BUNDLE_ALIAS_PROPERTY = "bundle_alias"; |
317 | public static final String WIDGET_TYPE_ALIAS_PROPERTY = ALIAS_PROPERTY; | 318 | public static final String WIDGET_TYPE_ALIAS_PROPERTY = ALIAS_PROPERTY; |
318 | public static final String WIDGET_TYPE_NAME_PROPERTY = "name"; | 319 | public static final String WIDGET_TYPE_NAME_PROPERTY = "name"; |
320 | + public static final String WIDGET_TYPE_IMAGE_PROPERTY = "image"; | ||
321 | + public static final String WIDGET_TYPE_DESCRIPTION_PROPERTY = "description"; | ||
319 | public static final String WIDGET_TYPE_DESCRIPTOR_PROPERTY = "descriptor"; | 322 | public static final String WIDGET_TYPE_DESCRIPTOR_PROPERTY = "descriptor"; |
320 | 323 | ||
321 | public static final String WIDGET_TYPE_BY_TENANT_AND_ALIASES_COLUMN_FAMILY_NAME = "widget_type_by_tenant_and_aliases"; | 324 | public static final String WIDGET_TYPE_BY_TENANT_AND_ALIASES_COLUMN_FAMILY_NAME = "widget_type_by_tenant_and_aliases"; |
@@ -52,6 +52,12 @@ public final class WidgetTypeEntity extends BaseSqlEntity<WidgetType> implement | @@ -52,6 +52,12 @@ public final class WidgetTypeEntity extends BaseSqlEntity<WidgetType> implement | ||
52 | @Column(name = ModelConstants.WIDGET_TYPE_NAME_PROPERTY) | 52 | @Column(name = ModelConstants.WIDGET_TYPE_NAME_PROPERTY) |
53 | private String name; | 53 | private String name; |
54 | 54 | ||
55 | + @Column(name = ModelConstants.WIDGET_TYPE_IMAGE_PROPERTY) | ||
56 | + private String image; | ||
57 | + | ||
58 | + @Column(name = ModelConstants.WIDGET_TYPE_DESCRIPTION_PROPERTY) | ||
59 | + private String description; | ||
60 | + | ||
55 | @Type(type="json") | 61 | @Type(type="json") |
56 | @Column(name = ModelConstants.WIDGET_TYPE_DESCRIPTOR_PROPERTY) | 62 | @Column(name = ModelConstants.WIDGET_TYPE_DESCRIPTOR_PROPERTY) |
57 | private JsonNode descriptor; | 63 | private JsonNode descriptor; |
@@ -71,6 +77,8 @@ public final class WidgetTypeEntity extends BaseSqlEntity<WidgetType> implement | @@ -71,6 +77,8 @@ public final class WidgetTypeEntity extends BaseSqlEntity<WidgetType> implement | ||
71 | this.bundleAlias = widgetType.getBundleAlias(); | 77 | this.bundleAlias = widgetType.getBundleAlias(); |
72 | this.alias = widgetType.getAlias(); | 78 | this.alias = widgetType.getAlias(); |
73 | this.name = widgetType.getName(); | 79 | this.name = widgetType.getName(); |
80 | + this.image = widgetType.getImage(); | ||
81 | + this.description = widgetType.getDescription(); | ||
74 | this.descriptor = widgetType.getDescriptor(); | 82 | this.descriptor = widgetType.getDescriptor(); |
75 | } | 83 | } |
76 | 84 | ||
@@ -84,6 +92,8 @@ public final class WidgetTypeEntity extends BaseSqlEntity<WidgetType> implement | @@ -84,6 +92,8 @@ public final class WidgetTypeEntity extends BaseSqlEntity<WidgetType> implement | ||
84 | widgetType.setBundleAlias(bundleAlias); | 92 | widgetType.setBundleAlias(bundleAlias); |
85 | widgetType.setAlias(alias); | 93 | widgetType.setAlias(alias); |
86 | widgetType.setName(name); | 94 | widgetType.setName(name); |
95 | + widgetType.setImage(image); | ||
96 | + widgetType.setDescription(description); | ||
87 | widgetType.setDescriptor(descriptor); | 97 | widgetType.setDescriptor(descriptor); |
88 | return widgetType; | 98 | return widgetType; |
89 | } | 99 | } |
@@ -48,6 +48,12 @@ public final class WidgetsBundleEntity extends BaseSqlEntity<WidgetsBundle> impl | @@ -48,6 +48,12 @@ public final class WidgetsBundleEntity extends BaseSqlEntity<WidgetsBundle> impl | ||
48 | @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY) | 48 | @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY) |
49 | private String searchText; | 49 | private String searchText; |
50 | 50 | ||
51 | + @Column(name = ModelConstants.WIDGETS_BUNDLE_IMAGE_PROPERTY) | ||
52 | + private String image; | ||
53 | + | ||
54 | + @Column(name = ModelConstants.WIDGETS_BUNDLE_DESCRIPTION) | ||
55 | + private String description; | ||
56 | + | ||
51 | public WidgetsBundleEntity() { | 57 | public WidgetsBundleEntity() { |
52 | super(); | 58 | super(); |
53 | } | 59 | } |
@@ -62,6 +68,8 @@ public final class WidgetsBundleEntity extends BaseSqlEntity<WidgetsBundle> impl | @@ -62,6 +68,8 @@ public final class WidgetsBundleEntity extends BaseSqlEntity<WidgetsBundle> impl | ||
62 | } | 68 | } |
63 | this.alias = widgetsBundle.getAlias(); | 69 | this.alias = widgetsBundle.getAlias(); |
64 | this.title = widgetsBundle.getTitle(); | 70 | this.title = widgetsBundle.getTitle(); |
71 | + this.image = widgetsBundle.getImage(); | ||
72 | + this.description = widgetsBundle.getDescription(); | ||
65 | } | 73 | } |
66 | 74 | ||
67 | @Override | 75 | @Override |
@@ -83,6 +91,8 @@ public final class WidgetsBundleEntity extends BaseSqlEntity<WidgetsBundle> impl | @@ -83,6 +91,8 @@ public final class WidgetsBundleEntity extends BaseSqlEntity<WidgetsBundle> impl | ||
83 | } | 91 | } |
84 | widgetsBundle.setAlias(alias); | 92 | widgetsBundle.setAlias(alias); |
85 | widgetsBundle.setTitle(title); | 93 | widgetsBundle.setTitle(title); |
94 | + widgetsBundle.setImage(image); | ||
95 | + widgetsBundle.setDescription(description); | ||
86 | return widgetsBundle; | 96 | return widgetsBundle; |
87 | } | 97 | } |
88 | } | 98 | } |
@@ -290,7 +290,9 @@ CREATE TABLE IF NOT EXISTS widget_type ( | @@ -290,7 +290,9 @@ CREATE TABLE IF NOT EXISTS widget_type ( | ||
290 | bundle_alias varchar(255), | 290 | bundle_alias varchar(255), |
291 | descriptor varchar(1000000), | 291 | descriptor varchar(1000000), |
292 | name varchar(255), | 292 | name varchar(255), |
293 | - tenant_id uuid | 293 | + tenant_id uuid, |
294 | + image varchar(1000000), | ||
295 | + description varchar(255) | ||
294 | ); | 296 | ); |
295 | 297 | ||
296 | CREATE TABLE IF NOT EXISTS widgets_bundle ( | 298 | CREATE TABLE IF NOT EXISTS widgets_bundle ( |
@@ -299,7 +301,9 @@ CREATE TABLE IF NOT EXISTS widgets_bundle ( | @@ -299,7 +301,9 @@ CREATE TABLE IF NOT EXISTS widgets_bundle ( | ||
299 | alias varchar(255), | 301 | alias varchar(255), |
300 | search_text varchar(255), | 302 | search_text varchar(255), |
301 | tenant_id uuid, | 303 | tenant_id uuid, |
302 | - title varchar(255) | 304 | + title varchar(255), |
305 | + image varchar(1000000), | ||
306 | + description varchar(255) | ||
303 | ); | 307 | ); |
304 | 308 | ||
305 | CREATE TABLE IF NOT EXISTS entity_view ( | 309 | CREATE TABLE IF NOT EXISTS entity_view ( |
@@ -315,7 +315,9 @@ CREATE TABLE IF NOT EXISTS widget_type ( | @@ -315,7 +315,9 @@ CREATE TABLE IF NOT EXISTS widget_type ( | ||
315 | bundle_alias varchar(255), | 315 | bundle_alias varchar(255), |
316 | descriptor varchar(1000000), | 316 | descriptor varchar(1000000), |
317 | name varchar(255), | 317 | name varchar(255), |
318 | - tenant_id uuid | 318 | + tenant_id uuid, |
319 | + image varchar(1000000), | ||
320 | + description varchar(255) | ||
319 | ); | 321 | ); |
320 | 322 | ||
321 | CREATE TABLE IF NOT EXISTS widgets_bundle ( | 323 | CREATE TABLE IF NOT EXISTS widgets_bundle ( |
@@ -324,7 +326,9 @@ CREATE TABLE IF NOT EXISTS widgets_bundle ( | @@ -324,7 +326,9 @@ CREATE TABLE IF NOT EXISTS widgets_bundle ( | ||
324 | alias varchar(255), | 326 | alias varchar(255), |
325 | search_text varchar(255), | 327 | search_text varchar(255), |
326 | tenant_id uuid, | 328 | tenant_id uuid, |
327 | - title varchar(255) | 329 | + title varchar(255), |
330 | + image varchar(1000000), | ||
331 | + description varchar(255) | ||
328 | ); | 332 | ); |
329 | 333 | ||
330 | CREATE TABLE IF NOT EXISTS entity_view ( | 334 | CREATE TABLE IF NOT EXISTS entity_view ( |
@@ -144,6 +144,8 @@ export class WidgetService { | @@ -144,6 +144,8 @@ export class WidgetService { | ||
144 | typeAlias: widgetTypeInfo.alias, | 144 | typeAlias: widgetTypeInfo.alias, |
145 | type: widgetTypeInfo.type, | 145 | type: widgetTypeInfo.type, |
146 | title: widgetTypeInfo.widgetName, | 146 | title: widgetTypeInfo.widgetName, |
147 | + image: widgetTypeInfo.image, | ||
148 | + description: widgetTypeInfo.description, | ||
147 | sizeX, | 149 | sizeX, |
148 | sizeY, | 150 | sizeY, |
149 | row: top, | 151 | row: top, |
@@ -44,6 +44,8 @@ export class WidgetEditorDashboardResolver implements Resolve<Dashboard> { | @@ -44,6 +44,8 @@ export class WidgetEditorDashboardResolver implements Resolve<Dashboard> { | ||
44 | typeAlias: 'customWidget', | 44 | typeAlias: 'customWidget', |
45 | type: editWidgetInfo.type, | 45 | type: editWidgetInfo.type, |
46 | title: 'My widget', | 46 | title: 'My widget', |
47 | + image: null, | ||
48 | + description: null, | ||
47 | sizeX: editWidgetInfo.sizeX * 2, | 49 | sizeX: editWidgetInfo.sizeX * 2, |
48 | sizeY: editWidgetInfo.sizeY * 2, | 50 | sizeY: editWidgetInfo.sizeY * 2, |
49 | row: 2, | 51 | row: 2, |
@@ -469,6 +469,8 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI | @@ -469,6 +469,8 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI | ||
469 | typeAlias: widgetInfo.alias, | 469 | typeAlias: widgetInfo.alias, |
470 | type: widgetInfo.type, | 470 | type: widgetInfo.type, |
471 | title: widgetInfo.widgetName, | 471 | title: widgetInfo.widgetName, |
472 | + image: widgetInfo.image, | ||
473 | + description: widgetInfo.description, | ||
472 | sizeX, | 474 | sizeX, |
473 | sizeY, | 475 | sizeY, |
474 | row: 0, | 476 | row: 0, |
@@ -253,7 +253,7 @@ | @@ -253,7 +253,7 @@ | ||
253 | fxFlex | 253 | fxFlex |
254 | required | 254 | required |
255 | [selectFirstBundle]="false" | 255 | [selectFirstBundle]="false" |
256 | - [ngModel]="widgetsBundle" | 256 | + [(ngModel)]="widgetsBundle" |
257 | (ngModelChange)="widgetsBundle = $event"> | 257 | (ngModelChange)="widgetsBundle = $event"> |
258 | </tb-widgets-bundle-select> | 258 | </tb-widgets-bundle-select> |
259 | </div> | 259 | </div> |
@@ -261,6 +261,7 @@ | @@ -261,6 +261,7 @@ | ||
261 | <tb-dashboard-widget-select *ngIf="isAddingWidget" | 261 | <tb-dashboard-widget-select *ngIf="isAddingWidget" |
262 | [aliasController]="dashboardCtx.aliasController" | 262 | [aliasController]="dashboardCtx.aliasController" |
263 | [widgetsBundle]="widgetsBundle" | 263 | [widgetsBundle]="widgetsBundle" |
264 | + (widgetsBundleSelected)="widgetBundleSelected($event)" | ||
264 | (widgetSelected)="addWidgetFromType($event)"> | 265 | (widgetSelected)="addWidgetFromType($event)"> |
265 | </tb-dashboard-widget-select> | 266 | </tb-dashboard-widget-select> |
266 | </tb-details-panel> | 267 | </tb-details-panel> |
@@ -865,6 +865,8 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | @@ -865,6 +865,8 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | ||
865 | typeAlias: widgetTypeInfo.alias, | 865 | typeAlias: widgetTypeInfo.alias, |
866 | type: widgetTypeInfo.type, | 866 | type: widgetTypeInfo.type, |
867 | title: 'New widget', | 867 | title: 'New widget', |
868 | + image: null, | ||
869 | + description: null, | ||
868 | sizeX: widgetTypeInfo.sizeX, | 870 | sizeX: widgetTypeInfo.sizeX, |
869 | sizeY: widgetTypeInfo.sizeY, | 871 | sizeY: widgetTypeInfo.sizeY, |
870 | config, | 872 | config, |
@@ -1111,4 +1113,8 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | @@ -1111,4 +1113,8 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | ||
1111 | } | 1113 | } |
1112 | return widgetContextActions; | 1114 | return widgetContextActions; |
1113 | } | 1115 | } |
1116 | + | ||
1117 | + widgetBundleSelected(bundle: WidgetsBundle){ | ||
1118 | + this.widgetsBundle = bundle; | ||
1119 | + } | ||
1114 | } | 1120 | } |
@@ -15,75 +15,47 @@ | @@ -15,75 +15,47 @@ | ||
15 | limitations under the License. | 15 | limitations under the License. |
16 | 16 | ||
17 | --> | 17 | --> |
18 | -<div> | ||
19 | - <mat-tab-group *ngIf="hasWidgetTypes()" class="tb-absolute-fill" fxFlex> | ||
20 | - <mat-tab *ngIf="timeseriesWidgetTypes.length" style="height: 100%;" label="{{ 'widget.timeseries' | translate }}"> | ||
21 | - <tb-dashboard [aliasController]="aliasController" | ||
22 | - [widgets]="timeseriesWidgetTypes" | ||
23 | - [widgetLayouts]="{}" | ||
24 | - [isEdit]="false" | ||
25 | - [isMobile]="true" | ||
26 | - [disableWidgetInteraction]="true" | ||
27 | - [isEditActionEnabled]="false" | ||
28 | - [isExportActionEnabled]="false" | ||
29 | - [isRemoveActionEnabled]="false" | ||
30 | - [callbacks]="callbacks"></tb-dashboard> | ||
31 | - </mat-tab> | ||
32 | - <mat-tab *ngIf="latestWidgetTypes.length" style="height: 100%;" label="{{ 'widget.latest-values' | translate }}"> | ||
33 | - <tb-dashboard [aliasController]="aliasController" | ||
34 | - [widgets]="latestWidgetTypes" | ||
35 | - [widgetLayouts]="{}" | ||
36 | - [isEdit]="false" | ||
37 | - [isMobile]="true" | ||
38 | - [disableWidgetInteraction]="true" | ||
39 | - [isEditActionEnabled]="false" | ||
40 | - [isExportActionEnabled]="false" | ||
41 | - [isRemoveActionEnabled]="false" | ||
42 | - [callbacks]="callbacks"></tb-dashboard> | ||
43 | - </mat-tab> | ||
44 | - <mat-tab *ngIf="rpcWidgetTypes.length" style="height: 100%;" label="{{ 'widget.rpc' | translate }}"> | ||
45 | - <tb-dashboard [aliasController]="aliasController" | ||
46 | - [widgets]="rpcWidgetTypes" | ||
47 | - [widgetLayouts]="{}" | ||
48 | - [isEdit]="false" | ||
49 | - [isMobile]="true" | ||
50 | - [disableWidgetInteraction]="true" | ||
51 | - [isEditActionEnabled]="false" | ||
52 | - [isExportActionEnabled]="false" | ||
53 | - [isRemoveActionEnabled]="false" | ||
54 | - [callbacks]="callbacks"></tb-dashboard> | ||
55 | - </mat-tab> | ||
56 | - <mat-tab *ngIf="alarmWidgetTypes.length" style="height: 100%;" label="{{ 'widget.alarm' | translate }}"> | ||
57 | - <tb-dashboard [aliasController]="aliasController" | ||
58 | - [widgets]="alarmWidgetTypes" | ||
59 | - [widgetLayouts]="{}" | ||
60 | - [isEdit]="false" | ||
61 | - [isMobile]="true" | ||
62 | - [disableWidgetInteraction]="true" | ||
63 | - [isEditActionEnabled]="false" | ||
64 | - [isExportActionEnabled]="false" | ||
65 | - [isRemoveActionEnabled]="false" | ||
66 | - [callbacks]="callbacks"></tb-dashboard> | ||
67 | - </mat-tab> | ||
68 | - <mat-tab *ngIf="staticWidgetTypes.length" style="height: 100%;" label="{{ 'widget.static' | translate }}"> | ||
69 | - <tb-dashboard [aliasController]="aliasController" | ||
70 | - [widgets]="staticWidgetTypes" | ||
71 | - [widgetLayouts]="{}" | ||
72 | - [isEdit]="false" | ||
73 | - [isMobile]="true" | ||
74 | - [disableWidgetInteraction]="true" | ||
75 | - [isEditActionEnabled]="false" | ||
76 | - [isExportActionEnabled]="false" | ||
77 | - [isRemoveActionEnabled]="false" | ||
78 | - [callbacks]="callbacks"></tb-dashboard> | ||
79 | - </mat-tab> | ||
80 | - </mat-tab-group> | 18 | +<div class="widget-select"> |
19 | + <div *ngIf="hasWidgetTypes()"> | ||
20 | + <button mat-raised-button (click)="backToSelectWidgetsBundle()" style="margin-bottom: 12px"> | ||
21 | + <mat-icon class="material-icons">undo</mat-icon> | ||
22 | + {{ 'widget.all-bundles' | translate }} | ||
23 | + </button> | ||
24 | + <div fxFlexFill fxLayoutGap="12px grid" fxLayout="row wrap"> | ||
25 | + <div *ngFor="let widget of widgets" class="mat-card-container"> | ||
26 | + <mat-card fxFlexFill fxLayout="row" fxLayoutGap="16px" (click)="onWidgetClicked($event, widget)"> | ||
27 | + <div fxFlex="45"> | ||
28 | + <img class="preview" src="https://material.angular.io/assets/img/examples/shiba2.jpg" alt="{{ widget.title }}"> | ||
29 | + </div> | ||
30 | + <div fxFlex fxLayout="column"> | ||
31 | + <mat-card-title>{{widget.title}}</mat-card-title> | ||
32 | + <mat-card-subtitle>{{ 'widget.' + widget.type | translate }}</mat-card-subtitle> | ||
33 | + <mat-card-content *ngIf="widgetsBundle.description"> | ||
34 | + {{ widget.description }} | ||
35 | + </mat-card-content> | ||
36 | + </div> | ||
37 | + </mat-card> | ||
38 | + </div> | ||
39 | + </div> | ||
40 | + </div> | ||
81 | <span translate *ngIf="widgetsBundle && !hasWidgetTypes()" | 41 | <span translate *ngIf="widgetsBundle && !hasWidgetTypes()" |
82 | style="display: flex;" | 42 | style="display: flex;" |
83 | fxLayoutAlign="center center" | 43 | fxLayoutAlign="center center" |
84 | class="mat-headline tb-absolute-fill">widgets-bundle.empty</span> | 44 | class="mat-headline tb-absolute-fill">widgets-bundle.empty</span> |
85 | - <span translate *ngIf="!widgetsBundle" | ||
86 | - style="display: flex;" | ||
87 | - fxLayoutAlign="center center" | ||
88 | - class="mat-headline tb-absolute-fill">widget.select-widgets-bundle</span> | 45 | + <div *ngIf="!widgetsBundle" fxFlexFill fxLayoutGap="12px grid" fxLayout="row wrap"> |
46 | + <div *ngFor="let widgetsBundle of widgetsBundles$ | async" class="mat-card-container"> | ||
47 | + <mat-card fxFlexFill fxLayout="row" fxLayoutGap="16px" (click)="selectBundle($event, widgetsBundle)"> | ||
48 | + <div fxFlex="45" *ngIf="isSystem(widgetsBundle)"> | ||
49 | + <img class="preview" src="https://material.angular.io/assets/img/examples/shiba2.jpg" alt="{{ widgetsBundle.title }}"> | ||
50 | + </div> | ||
51 | + <div fxFlex fxLayout="column"> | ||
52 | + <mat-card-title>{{ widgetsBundle.title }}</mat-card-title> | ||
53 | + <mat-card-subtitle *ngIf="isSystem(widgetsBundle)" translate>widgets-bundle.system</mat-card-subtitle> | ||
54 | + <mat-card-content *ngIf="widgetsBundle.description"> | ||
55 | + {{ widgetsBundle.description }} | ||
56 | + </mat-card-content> | ||
57 | + </div> | ||
58 | + </mat-card> | ||
59 | + </div> | ||
60 | + </div> | ||
89 | </div> | 61 | </div> |
@@ -13,10 +13,41 @@ | @@ -13,10 +13,41 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | -:host ::ng-deep { | ||
17 | - .mat-tab-group { | ||
18 | - .mat-tab-body-wrapper { | ||
19 | - height: 100%; | 16 | +@import '../../../../../scss/constants'; |
17 | + | ||
18 | +:host{ | ||
19 | + .widget-select { | ||
20 | + min-height: 100%; | ||
21 | + padding: 12px 0 12px 12px; | ||
22 | + background-color: #cfd8dc; | ||
23 | + | ||
24 | + .mat-card-container { | ||
25 | + flex: 0 0 100%; | ||
26 | + max-width: 100%; | ||
27 | + | ||
28 | + .mat-card { | ||
29 | + cursor: pointer; | ||
30 | + | ||
31 | + .preview { | ||
32 | + max-width: 100%; | ||
33 | + max-height: 100%; | ||
34 | + object-fit: contain; | ||
35 | + } | ||
36 | + } | ||
37 | + } | ||
38 | + | ||
39 | + @media #{$mat-gt-xs} { | ||
40 | + .mat-card-container { | ||
41 | + flex: 0 0 50%; | ||
42 | + max-width: 50%; | ||
43 | + } | ||
44 | + } | ||
45 | + | ||
46 | + @media screen and (min-width: 2000px) { | ||
47 | + .mat-card-container { | ||
48 | + flex: 0 0 33.333333%; | ||
49 | + max-width: 33.333333%; | ||
50 | + } | ||
20 | } | 51 | } |
21 | } | 52 | } |
22 | } | 53 | } |
@@ -19,9 +19,10 @@ import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; | @@ -19,9 +19,10 @@ import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; | ||
19 | import { IAliasController } from '@core/api/widget-api.models'; | 19 | import { IAliasController } from '@core/api/widget-api.models'; |
20 | import { NULL_UUID } from '@shared/models/id/has-uuid'; | 20 | import { NULL_UUID } from '@shared/models/id/has-uuid'; |
21 | import { WidgetService } from '@core/http/widget.service'; | 21 | import { WidgetService } from '@core/http/widget.service'; |
22 | -import { Widget, widgetType } from '@shared/models/widget.models'; | 22 | +import { Widget } from '@shared/models/widget.models'; |
23 | import { toWidgetInfo } from '@home/models/widget-component.models'; | 23 | import { toWidgetInfo } from '@home/models/widget-component.models'; |
24 | -import { DashboardCallbacks } from '../../models/dashboard-component.models'; | 24 | +import { share } from 'rxjs/operators'; |
25 | +import { Observable } from 'rxjs'; | ||
25 | 26 | ||
26 | @Component({ | 27 | @Component({ |
27 | selector: 'tb-dashboard-widget-select', | 28 | selector: 'tb-dashboard-widget-select', |
@@ -39,20 +40,20 @@ export class DashboardWidgetSelectComponent implements OnInit, OnChanges { | @@ -39,20 +40,20 @@ export class DashboardWidgetSelectComponent implements OnInit, OnChanges { | ||
39 | @Output() | 40 | @Output() |
40 | widgetSelected: EventEmitter<Widget> = new EventEmitter<Widget>(); | 41 | widgetSelected: EventEmitter<Widget> = new EventEmitter<Widget>(); |
41 | 42 | ||
42 | - timeseriesWidgetTypes: Array<Widget> = []; | ||
43 | - latestWidgetTypes: Array<Widget> = []; | ||
44 | - rpcWidgetTypes: Array<Widget> = []; | ||
45 | - alarmWidgetTypes: Array<Widget> = []; | ||
46 | - staticWidgetTypes: Array<Widget> = []; | 43 | + @Output() |
44 | + widgetsBundleSelected: EventEmitter<WidgetsBundle> = new EventEmitter<WidgetsBundle>(); | ||
45 | + | ||
46 | + widgets: Array<Widget> = []; | ||
47 | 47 | ||
48 | - callbacks: DashboardCallbacks = { | ||
49 | - onWidgetClicked: this.onWidgetClicked.bind(this) | ||
50 | - }; | 48 | + widgetsBundles$: Observable<Array<WidgetsBundle>>; |
51 | 49 | ||
52 | constructor(private widgetsService: WidgetService) { | 50 | constructor(private widgetsService: WidgetService) { |
53 | } | 51 | } |
54 | 52 | ||
55 | ngOnInit(): void { | 53 | ngOnInit(): void { |
54 | + this.widgetsBundles$ = this.widgetsService.getAllWidgetsBundles().pipe( | ||
55 | + share() | ||
56 | + ); | ||
56 | } | 57 | } |
57 | 58 | ||
58 | ngOnChanges(changes: SimpleChanges): void { | 59 | ngOnChanges(changes: SimpleChanges): void { |
@@ -67,11 +68,7 @@ export class DashboardWidgetSelectComponent implements OnInit, OnChanges { | @@ -67,11 +68,7 @@ export class DashboardWidgetSelectComponent implements OnInit, OnChanges { | ||
67 | } | 68 | } |
68 | 69 | ||
69 | private loadLibrary() { | 70 | private loadLibrary() { |
70 | - this.timeseriesWidgetTypes.length = 0; | ||
71 | - this.latestWidgetTypes.length = 0; | ||
72 | - this.rpcWidgetTypes.length = 0; | ||
73 | - this.alarmWidgetTypes.length = 0; | ||
74 | - this.staticWidgetTypes.length = 0; | 71 | + this.widgets.length = 0; |
75 | const bundleAlias = this.widgetsBundle.alias; | 72 | const bundleAlias = this.widgetsBundle.alias; |
76 | const isSystem = this.widgetsBundle.tenantId.id === NULL_UUID; | 73 | const isSystem = this.widgetsBundle.tenantId.id === NULL_UUID; |
77 | this.widgetsService.getBundleWidgetTypes(bundleAlias, | 74 | this.widgetsService.getBundleWidgetTypes(bundleAlias, |
@@ -88,6 +85,8 @@ export class DashboardWidgetSelectComponent implements OnInit, OnChanges { | @@ -88,6 +85,8 @@ export class DashboardWidgetSelectComponent implements OnInit, OnChanges { | ||
88 | typeAlias: widgetTypeInfo.alias, | 85 | typeAlias: widgetTypeInfo.alias, |
89 | type: widgetTypeInfo.type, | 86 | type: widgetTypeInfo.type, |
90 | title: widgetTypeInfo.widgetName, | 87 | title: widgetTypeInfo.widgetName, |
88 | + image: widgetTypeInfo.image, | ||
89 | + description: widgetTypeInfo.description, | ||
91 | sizeX: widgetTypeInfo.sizeX, | 90 | sizeX: widgetTypeInfo.sizeX, |
92 | sizeY: widgetTypeInfo.sizeY, | 91 | sizeY: widgetTypeInfo.sizeY, |
93 | row: top, | 92 | row: top, |
@@ -95,39 +94,35 @@ export class DashboardWidgetSelectComponent implements OnInit, OnChanges { | @@ -95,39 +94,35 @@ export class DashboardWidgetSelectComponent implements OnInit, OnChanges { | ||
95 | config: JSON.parse(widgetTypeInfo.defaultConfig) | 94 | config: JSON.parse(widgetTypeInfo.defaultConfig) |
96 | }; | 95 | }; |
97 | widget.config.title = widgetTypeInfo.widgetName; | 96 | widget.config.title = widgetTypeInfo.widgetName; |
98 | - switch (widgetTypeInfo.type) { | ||
99 | - case widgetType.timeseries: | ||
100 | - this.timeseriesWidgetTypes.push(widget); | ||
101 | - break; | ||
102 | - case widgetType.latest: | ||
103 | - this.latestWidgetTypes.push(widget); | ||
104 | - break; | ||
105 | - case widgetType.rpc: | ||
106 | - this.rpcWidgetTypes.push(widget); | ||
107 | - break; | ||
108 | - case widgetType.alarm: | ||
109 | - this.alarmWidgetTypes.push(widget); | ||
110 | - break; | ||
111 | - case widgetType.static: | ||
112 | - this.staticWidgetTypes.push(widget); | ||
113 | - break; | ||
114 | - } | 97 | + this.widgets.push(widget); |
115 | top += widget.sizeY; | 98 | top += widget.sizeY; |
116 | }); | 99 | }); |
117 | } | 100 | } |
118 | ); | 101 | ); |
119 | } | 102 | } |
120 | 103 | ||
121 | - hasWidgetTypes() { | ||
122 | - return this.timeseriesWidgetTypes.length > 0 || | ||
123 | - this.latestWidgetTypes.length > 0 || | ||
124 | - this.rpcWidgetTypes.length > 0 || | ||
125 | - this.alarmWidgetTypes.length > 0 || | ||
126 | - this.staticWidgetTypes.length > 0; | 104 | + hasWidgetTypes(): boolean { |
105 | + return this.widgets.length > 0; | ||
127 | } | 106 | } |
128 | 107 | ||
129 | - private onWidgetClicked($event: Event, widget: Widget, index: number): void { | 108 | + onWidgetClicked($event: Event, widget: Widget): void { |
130 | this.widgetSelected.emit(widget); | 109 | this.widgetSelected.emit(widget); |
131 | } | 110 | } |
132 | 111 | ||
112 | + isSystem(item: WidgetsBundle): boolean { | ||
113 | + return item && item.tenantId.id === NULL_UUID; | ||
114 | + } | ||
115 | + | ||
116 | + selectBundle($event: Event, bundle: WidgetsBundle) { | ||
117 | + $event.preventDefault(); | ||
118 | + this.widgetsBundle = bundle; | ||
119 | + this.widgetsBundleSelected.emit(bundle); | ||
120 | + } | ||
121 | + | ||
122 | + backToSelectWidgetsBundle() { | ||
123 | + this.widgetsBundle = null; | ||
124 | + this.widgets.length = 0; | ||
125 | + this.widgetsBundleSelected.emit(null); | ||
126 | + } | ||
127 | + | ||
133 | } | 128 | } |
@@ -25,7 +25,6 @@ import { | @@ -25,7 +25,6 @@ import { | ||
25 | Injector, | 25 | Injector, |
26 | Input, | 26 | Input, |
27 | OnDestroy, | 27 | OnDestroy, |
28 | - OnInit, | ||
29 | Output, | 28 | Output, |
30 | QueryList, | 29 | QueryList, |
31 | ViewChild, | 30 | ViewChild, |
@@ -52,7 +51,7 @@ import { deepClone } from '@core/utils'; | @@ -52,7 +51,7 @@ import { deepClone } from '@core/utils'; | ||
52 | styleUrls: ['./entity-details-panel.component.scss'], | 51 | styleUrls: ['./entity-details-panel.component.scss'], |
53 | changeDetection: ChangeDetectionStrategy.OnPush | 52 | changeDetection: ChangeDetectionStrategy.OnPush |
54 | }) | 53 | }) |
55 | -export class EntityDetailsPanelComponent extends PageComponent implements OnInit, AfterViewInit, OnDestroy { | 54 | +export class EntityDetailsPanelComponent extends PageComponent implements AfterViewInit, OnDestroy { |
56 | 55 | ||
57 | @Output() | 56 | @Output() |
58 | closeEntityDetails = new EventEmitter<void>(); | 57 | closeEntityDetails = new EventEmitter<void>(); |
@@ -140,10 +139,6 @@ export class EntityDetailsPanelComponent extends PageComponent implements OnInit | @@ -140,10 +139,6 @@ export class EntityDetailsPanelComponent extends PageComponent implements OnInit | ||
140 | return this.isEditValue; | 139 | return this.isEditValue; |
141 | } | 140 | } |
142 | 141 | ||
143 | - ngOnInit(): void { | ||
144 | - this.init(); | ||
145 | - } | ||
146 | - | ||
147 | private init() { | 142 | private init() { |
148 | this.translations = this.entitiesTableConfig.entityTranslations; | 143 | this.translations = this.entitiesTableConfig.entityTranslations; |
149 | this.resources = this.entitiesTableConfig.entityResources; | 144 | this.resources = this.entitiesTableConfig.entityResources; |
@@ -104,6 +104,8 @@ export class WidgetComponentService { | @@ -104,6 +104,8 @@ export class WidgetComponentService { | ||
104 | controllerScript: this.utils.editWidgetInfo.controllerScript, | 104 | controllerScript: this.utils.editWidgetInfo.controllerScript, |
105 | settingsSchema: this.utils.editWidgetInfo.settingsSchema, | 105 | settingsSchema: this.utils.editWidgetInfo.settingsSchema, |
106 | dataKeySettingsSchema: this.utils.editWidgetInfo.dataKeySettingsSchema, | 106 | dataKeySettingsSchema: this.utils.editWidgetInfo.dataKeySettingsSchema, |
107 | + image: this.utils.editWidgetInfo.image, | ||
108 | + description: this.utils.editWidgetInfo.description, | ||
107 | defaultConfig: this.utils.editWidgetInfo.defaultConfig | 109 | defaultConfig: this.utils.editWidgetInfo.defaultConfig |
108 | }, new WidgetTypeId('1'), new TenantId( NULL_UUID ), 'customWidgetBundle' | 110 | }, new WidgetTypeId('1'), new TenantId( NULL_UUID ), 'customWidgetBundle' |
109 | ); | 111 | ); |
@@ -347,6 +347,8 @@ export interface WidgetInfo extends WidgetTypeDescriptor, WidgetControllerDescri | @@ -347,6 +347,8 @@ export interface WidgetInfo extends WidgetTypeDescriptor, WidgetControllerDescri | ||
347 | alias: string; | 347 | alias: string; |
348 | typeSettingsSchema?: string | any; | 348 | typeSettingsSchema?: string | any; |
349 | typeDataKeySettingsSchema?: string | any; | 349 | typeDataKeySettingsSchema?: string | any; |
350 | + image: string; | ||
351 | + description: string; | ||
350 | componentFactory?: ComponentFactory<IDynamicWidgetComponent>; | 352 | componentFactory?: ComponentFactory<IDynamicWidgetComponent>; |
351 | } | 353 | } |
352 | 354 | ||
@@ -375,6 +377,8 @@ export const MissingWidgetType: WidgetInfo = { | @@ -375,6 +377,8 @@ export const MissingWidgetType: WidgetInfo = { | ||
375 | controllerScript: 'self.onInit = function() {}', | 377 | controllerScript: 'self.onInit = function() {}', |
376 | settingsSchema: '{}\n', | 378 | settingsSchema: '{}\n', |
377 | dataKeySettingsSchema: '{}\n', | 379 | dataKeySettingsSchema: '{}\n', |
380 | + image: null, | ||
381 | + description: null, | ||
378 | defaultConfig: '{\n' + | 382 | defaultConfig: '{\n' + |
379 | '"title": "Widget type not found",\n' + | 383 | '"title": "Widget type not found",\n' + |
380 | '"datasources": [],\n' + | 384 | '"datasources": [],\n' + |
@@ -398,6 +402,8 @@ export const ErrorWidgetType: WidgetInfo = { | @@ -398,6 +402,8 @@ export const ErrorWidgetType: WidgetInfo = { | ||
398 | controllerScript: 'self.onInit = function() {}', | 402 | controllerScript: 'self.onInit = function() {}', |
399 | settingsSchema: '{}\n', | 403 | settingsSchema: '{}\n', |
400 | dataKeySettingsSchema: '{}\n', | 404 | dataKeySettingsSchema: '{}\n', |
405 | + image: null, | ||
406 | + description: null, | ||
401 | defaultConfig: '{\n' + | 407 | defaultConfig: '{\n' + |
402 | '"title": "Widget failed to load",\n' + | 408 | '"title": "Widget failed to load",\n' + |
403 | '"datasources": [],\n' + | 409 | '"datasources": [],\n' + |
@@ -424,6 +430,8 @@ export interface WidgetTypeInstance { | @@ -424,6 +430,8 @@ export interface WidgetTypeInstance { | ||
424 | export function toWidgetInfo(widgetTypeEntity: WidgetType): WidgetInfo { | 430 | export function toWidgetInfo(widgetTypeEntity: WidgetType): WidgetInfo { |
425 | return { | 431 | return { |
426 | widgetName: widgetTypeEntity.name, | 432 | widgetName: widgetTypeEntity.name, |
433 | + image: widgetTypeEntity.image, | ||
434 | + description: widgetTypeEntity.description, | ||
427 | alias: widgetTypeEntity.alias, | 435 | alias: widgetTypeEntity.alias, |
428 | type: widgetTypeEntity.descriptor.type, | 436 | type: widgetTypeEntity.descriptor.type, |
429 | sizeX: widgetTypeEntity.descriptor.sizeX, | 437 | sizeX: widgetTypeEntity.descriptor.sizeX, |
@@ -457,6 +465,8 @@ export function toWidgetType(widgetInfo: WidgetInfo, id: WidgetTypeId, tenantId: | @@ -457,6 +465,8 @@ export function toWidgetType(widgetInfo: WidgetInfo, id: WidgetTypeId, tenantId: | ||
457 | bundleAlias, | 465 | bundleAlias, |
458 | alias: widgetInfo.alias, | 466 | alias: widgetInfo.alias, |
459 | name: widgetInfo.widgetName, | 467 | name: widgetInfo.widgetName, |
468 | + image: widgetInfo.image, | ||
469 | + description: widgetInfo.description, | ||
460 | descriptor | 470 | descriptor |
461 | }; | 471 | }; |
462 | } | 472 | } |
@@ -117,7 +117,7 @@ | @@ -117,7 +117,7 @@ | ||
117 | <div #topPanel class="tb-split tb-split-vertical"> | 117 | <div #topPanel class="tb-split tb-split-vertical"> |
118 | <div #topLeftPanel class="tb-split tb-content"> | 118 | <div #topLeftPanel class="tb-split tb-content"> |
119 | <mat-tab-group selectedIndex="1" dynamicHeight="true" style="width: 100%; height: 100%;"> | 119 | <mat-tab-group selectedIndex="1" dynamicHeight="true" style="width: 100%; height: 100%;"> |
120 | - <mat-tab label="{{ 'widget.resources' | translate }}" style="width: 100%; height: 100%;"> | 120 | + <mat-tab label="{{ 'widget.resources' | translate }}"> |
121 | <div class="tb-resize-container" style="background-color: #fff;"> | 121 | <div class="tb-resize-container" style="background-color: #fff;"> |
122 | <div class="mat-padding"> | 122 | <div class="mat-padding"> |
123 | <div fxFlex fxLayout="row" style="max-height: 40px;" | 123 | <div fxFlex fxLayout="row" style="max-height: 40px;" |
@@ -153,7 +153,7 @@ | @@ -153,7 +153,7 @@ | ||
153 | </div> | 153 | </div> |
154 | </div> | 154 | </div> |
155 | </mat-tab> | 155 | </mat-tab> |
156 | - <mat-tab label="{{ 'widget.html' | translate }}" style="width: 100%; height: 100%;"> | 156 | + <mat-tab label="{{ 'widget.html' | translate }}"> |
157 | <div class="tb-resize-container" tb-fullscreen [fullscreen]="htmlFullscreen"> | 157 | <div class="tb-resize-container" tb-fullscreen [fullscreen]="htmlFullscreen"> |
158 | <div class="tb-editor-area-title-panel"> | 158 | <div class="tb-editor-area-title-panel"> |
159 | <button mat-button (click)="beautifyHtml()"> | 159 | <button mat-button (click)="beautifyHtml()"> |
@@ -169,7 +169,7 @@ | @@ -169,7 +169,7 @@ | ||
169 | <div #htmlInput></div> | 169 | <div #htmlInput></div> |
170 | </div> | 170 | </div> |
171 | </mat-tab> | 171 | </mat-tab> |
172 | - <mat-tab label="{{ 'widget.css' | translate }}" style="width: 100%; height: 100%;"> | 172 | + <mat-tab label="{{ 'widget.css' | translate }}"> |
173 | <div class="tb-resize-container" tb-fullscreen [fullscreen]="cssFullscreen"> | 173 | <div class="tb-resize-container" tb-fullscreen [fullscreen]="cssFullscreen"> |
174 | <div class="tb-editor-area-title-panel"> | 174 | <div class="tb-editor-area-title-panel"> |
175 | <button mat-button (click)="beautifyCss()"> | 175 | <button mat-button (click)="beautifyCss()"> |
@@ -189,7 +189,7 @@ | @@ -189,7 +189,7 @@ | ||
189 | </div> | 189 | </div> |
190 | <div #topRightPanel class="tb-split tb-content"> | 190 | <div #topRightPanel class="tb-split tb-content"> |
191 | <mat-tab-group dynamicHeight="true" style="width: 100%; height: 100%;"> | 191 | <mat-tab-group dynamicHeight="true" style="width: 100%; height: 100%;"> |
192 | - <mat-tab label="{{ 'widget.settings-schema' | translate }}" style="width: 100%; height: 100%;"> | 192 | + <mat-tab label="{{ 'widget.settings-schema' | translate }}"> |
193 | <div class="tb-resize-container" tb-fullscreen [fullscreen]="jsonSettingsFullscreen"> | 193 | <div class="tb-resize-container" tb-fullscreen [fullscreen]="jsonSettingsFullscreen"> |
194 | <div class="tb-editor-area-title-panel"> | 194 | <div class="tb-editor-area-title-panel"> |
195 | <button mat-button (click)="beautifyJson()"> | 195 | <button mat-button (click)="beautifyJson()"> |
@@ -205,7 +205,7 @@ | @@ -205,7 +205,7 @@ | ||
205 | <div #settingsJsonInput></div> | 205 | <div #settingsJsonInput></div> |
206 | </div> | 206 | </div> |
207 | </mat-tab> | 207 | </mat-tab> |
208 | - <mat-tab label="{{ 'widget.datakey-settings-schema' | translate }}" style="width: 100%; height: 100%;"> | 208 | + <mat-tab label="{{ 'widget.datakey-settings-schema' | translate }}"> |
209 | <div class="tb-resize-container" tb-fullscreen [fullscreen]="jsonDataKeySettingsFullscreen"> | 209 | <div class="tb-resize-container" tb-fullscreen [fullscreen]="jsonDataKeySettingsFullscreen"> |
210 | <div class="tb-editor-area-title-panel"> | 210 | <div class="tb-editor-area-title-panel"> |
211 | <button mat-button (click)="beautifyDataKeyJson()"> | 211 | <button mat-button (click)="beautifyDataKeyJson()"> |
@@ -221,6 +221,25 @@ | @@ -221,6 +221,25 @@ | ||
221 | <div #dataKeySettingsJsonInput></div> | 221 | <div #dataKeySettingsJsonInput></div> |
222 | </div> | 222 | </div> |
223 | </mat-tab> | 223 | </mat-tab> |
224 | + <mat-tab label="{{ 'widget.widget-settings' | translate }}"> | ||
225 | + <div class="tb-resize-container" style="background-color: #fff;"> | ||
226 | + <div class="mat-padding"> | ||
227 | + <tb-image-input fxFlex label="{{'widget.image-preview' | translate}}" | ||
228 | + maxSizeByte="524288" | ||
229 | + [(ngModel)]="widget.image" | ||
230 | + (ngModelChange)="isDirty = true" > | ||
231 | + </tb-image-input> | ||
232 | + <mat-form-field class="mat-block"> | ||
233 | + <mat-label translate>widget.description</mat-label> | ||
234 | + <textarea matInput #descriptionInput | ||
235 | + [(ngModel)]="widget.description" | ||
236 | + (ngModelChange)="isDirty = true" | ||
237 | + rows="2" maxlength="255"></textarea> | ||
238 | + <mat-hint align="end">{{descriptionInput.value?.length || 0}}/255</mat-hint> | ||
239 | + </mat-form-field> | ||
240 | + </div> | ||
241 | + </div> | ||
242 | + </mat-tab> | ||
224 | </mat-tab-group> | 243 | </mat-tab-group> |
225 | </div> | 244 | </div> |
226 | </div> | 245 | </div> |
@@ -45,6 +45,16 @@ | @@ -45,6 +45,16 @@ | ||
45 | {{ 'widgets-bundle.title-required' | translate }} | 45 | {{ 'widgets-bundle.title-required' | translate }} |
46 | </mat-error> | 46 | </mat-error> |
47 | </mat-form-field> | 47 | </mat-form-field> |
48 | + <tb-image-input fxFlex | ||
49 | + label="{{'widgets-bundle.image-preview' | translate}}" | ||
50 | + maxSizeByte="524288" | ||
51 | + formControlName="image"> | ||
52 | + </tb-image-input> | ||
53 | + <mat-form-field class="mat-block"> | ||
54 | + <mat-label translate>widgets-bundle.description</mat-label> | ||
55 | + <textarea matInput formControlName="description" rows="2" maxlength="255" #descriptionInput></textarea> | ||
56 | + <mat-hint align="end">{{descriptionInput.value?.length || 0}}/255</mat-hint> | ||
57 | + </mat-form-field> | ||
48 | </fieldset> | 58 | </fieldset> |
49 | </form> | 59 | </form> |
50 | </div> | 60 | </div> |
@@ -47,12 +47,18 @@ export class WidgetsBundleComponent extends EntityComponent<WidgetsBundle> { | @@ -47,12 +47,18 @@ export class WidgetsBundleComponent extends EntityComponent<WidgetsBundle> { | ||
47 | buildForm(entity: WidgetsBundle): FormGroup { | 47 | buildForm(entity: WidgetsBundle): FormGroup { |
48 | return this.fb.group( | 48 | return this.fb.group( |
49 | { | 49 | { |
50 | - title: [entity ? entity.title : '', [Validators.required]] | 50 | + title: [entity ? entity.title : '', [Validators.required]], |
51 | + image: [entity ? entity.image : ''], | ||
52 | + description: [entity ? entity.description : '', Validators.maxLength(255)] | ||
51 | } | 53 | } |
52 | ); | 54 | ); |
53 | } | 55 | } |
54 | 56 | ||
55 | updateForm(entity: WidgetsBundle) { | 57 | updateForm(entity: WidgetsBundle) { |
56 | - this.entityForm.patchValue({title: entity.title}); | 58 | + this.entityForm.patchValue({ |
59 | + title: entity.title, | ||
60 | + image: entity.image, | ||
61 | + description: entity.description | ||
62 | + }); | ||
57 | } | 63 | } |
58 | } | 64 | } |
@@ -24,6 +24,9 @@ import { coerceBooleanProperty } from '@angular/cdk/coercion'; | @@ -24,6 +24,9 @@ import { coerceBooleanProperty } from '@angular/cdk/coercion'; | ||
24 | import { FlowDirective } from '@flowjs/ngx-flow'; | 24 | import { FlowDirective } from '@flowjs/ngx-flow'; |
25 | import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; | 25 | import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; |
26 | import { UtilsService } from '@core/services/utils.service'; | 26 | import { UtilsService } from '@core/services/utils.service'; |
27 | +import { DialogService } from '@core/services/dialog.service'; | ||
28 | +import { TranslateService } from '@ngx-translate/core'; | ||
29 | +import { FileSizePipe } from '@shared/pipe/file-size.pipe'; | ||
27 | 30 | ||
28 | @Component({ | 31 | @Component({ |
29 | selector: 'tb-image-input', | 32 | selector: 'tb-image-input', |
@@ -42,6 +45,9 @@ export class ImageInputComponent extends PageComponent implements AfterViewInit, | @@ -42,6 +45,9 @@ export class ImageInputComponent extends PageComponent implements AfterViewInit, | ||
42 | @Input() | 45 | @Input() |
43 | label: string; | 46 | label: string; |
44 | 47 | ||
48 | + @Input() | ||
49 | + maxSizeByte: number; | ||
50 | + | ||
45 | private requiredValue: boolean; | 51 | private requiredValue: boolean; |
46 | 52 | ||
47 | get required(): boolean { | 53 | get required(): boolean { |
@@ -80,7 +86,10 @@ export class ImageInputComponent extends PageComponent implements AfterViewInit, | @@ -80,7 +86,10 @@ export class ImageInputComponent extends PageComponent implements AfterViewInit, | ||
80 | 86 | ||
81 | constructor(protected store: Store<AppState>, | 87 | constructor(protected store: Store<AppState>, |
82 | private utils: UtilsService, | 88 | private utils: UtilsService, |
83 | - private sanitizer: DomSanitizer) { | 89 | + private sanitizer: DomSanitizer, |
90 | + private dialog: DialogService, | ||
91 | + private translate: TranslateService, | ||
92 | + private fileSize: FileSizePipe) { | ||
84 | super(store); | 93 | super(store); |
85 | } | 94 | } |
86 | 95 | ||
@@ -88,6 +97,15 @@ export class ImageInputComponent extends PageComponent implements AfterViewInit, | @@ -88,6 +97,15 @@ export class ImageInputComponent extends PageComponent implements AfterViewInit, | ||
88 | this.autoUploadSubscription = this.flow.events$.subscribe(event => { | 97 | this.autoUploadSubscription = this.flow.events$.subscribe(event => { |
89 | if (event.type === 'fileAdded') { | 98 | if (event.type === 'fileAdded') { |
90 | const file = (event.event[0] as flowjs.FlowFile).file; | 99 | const file = (event.event[0] as flowjs.FlowFile).file; |
100 | + if (this.maxSizeByte && this.maxSizeByte < file.size) { | ||
101 | + this.dialog.alert( | ||
102 | + this.translate.instant('dashboard.cannot-upload-file'), | ||
103 | + this.translate.instant('dashboard.maximum-upload-file-size', {size: this.fileSize.transform(this.maxSizeByte)}) | ||
104 | + ).subscribe( | ||
105 | + () => { } | ||
106 | + ); | ||
107 | + return false; | ||
108 | + } | ||
91 | const reader = new FileReader(); | 109 | const reader = new FileReader(); |
92 | reader.onload = (loadEvent) => { | 110 | reader.onload = (loadEvent) => { |
93 | if (typeof reader.result === 'string' && reader.result.startsWith('data:image/')) { | 111 | if (typeof reader.result === 'string' && reader.result.startsWith('data:image/')) { |
@@ -63,7 +63,7 @@ export const widgetTypesData = new Map<widgetType, WidgetTypeData>( | @@ -63,7 +63,7 @@ export const widgetTypesData = new Map<widgetType, WidgetTypeData>( | ||
63 | [ | 63 | [ |
64 | widgetType.latest, | 64 | widgetType.latest, |
65 | { | 65 | { |
66 | - name: 'widget.latest-values', | 66 | + name: 'widget.latest', |
67 | icon: 'track_changes', | 67 | icon: 'track_changes', |
68 | configHelpLinkId: 'widgetsConfigLatest', | 68 | configHelpLinkId: 'widgetsConfigLatest', |
69 | template: { | 69 | template: { |
@@ -169,6 +169,8 @@ export interface WidgetType extends BaseData<WidgetTypeId> { | @@ -169,6 +169,8 @@ export interface WidgetType extends BaseData<WidgetTypeId> { | ||
169 | bundleAlias: string; | 169 | bundleAlias: string; |
170 | alias: string; | 170 | alias: string; |
171 | name: string; | 171 | name: string; |
172 | + image: string; | ||
173 | + description: string; | ||
172 | descriptor: WidgetTypeDescriptor; | 174 | descriptor: WidgetTypeDescriptor; |
173 | } | 175 | } |
174 | 176 | ||
@@ -408,6 +410,8 @@ export interface Widget { | @@ -408,6 +410,8 @@ export interface Widget { | ||
408 | sizeY: number; | 410 | sizeY: number; |
409 | row: number; | 411 | row: number; |
410 | col: number; | 412 | col: number; |
413 | + image: string; | ||
414 | + description: string; | ||
411 | config: WidgetConfig; | 415 | config: WidgetConfig; |
412 | } | 416 | } |
413 | 417 | ||
@@ -426,7 +430,7 @@ export interface JsonSchema { | @@ -426,7 +430,7 @@ export interface JsonSchema { | ||
426 | export interface JsonSettingsSchema { | 430 | export interface JsonSettingsSchema { |
427 | schema?: JsonSchema; | 431 | schema?: JsonSchema; |
428 | form?: any[]; | 432 | form?: any[]; |
429 | - groupInfoes?: GroupInfo[] | 433 | + groupInfoes?: GroupInfo[]; |
430 | } | 434 | } |
431 | 435 | ||
432 | export interface WidgetPosition { | 436 | export interface WidgetPosition { |
@@ -23,4 +23,5 @@ export interface WidgetsBundle extends BaseData<WidgetsBundleId> { | @@ -23,4 +23,5 @@ export interface WidgetsBundle extends BaseData<WidgetsBundleId> { | ||
23 | alias: string; | 23 | alias: string; |
24 | title: string; | 24 | title: string; |
25 | image: string; | 25 | image: string; |
26 | + description: string; | ||
26 | } | 27 | } |
ui-ngx/src/app/shared/pipe/file-size.pipe.ts
0 → 100644
1 | +/// | ||
2 | +/// Copyright © 2016-2021 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import { Pipe, PipeTransform } from '@angular/core'; | ||
18 | + | ||
19 | +type unit = 'bytes' | 'KB' | 'MB' | 'GB' | 'TB' | 'PB'; | ||
20 | +type unitPrecisionMap = { | ||
21 | + [u in unit]: number; | ||
22 | +}; | ||
23 | + | ||
24 | +const defaultPrecisionMap: unitPrecisionMap = { | ||
25 | + bytes: 0, | ||
26 | + KB: 0, | ||
27 | + MB: 1, | ||
28 | + GB: 1, | ||
29 | + TB: 2, | ||
30 | + PB: 2 | ||
31 | +}; | ||
32 | + | ||
33 | +@Pipe({ name: 'fileSize' }) | ||
34 | +export class FileSizePipe implements PipeTransform { | ||
35 | + private readonly units: unit[] = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB']; | ||
36 | + | ||
37 | + transform(bytes: number = 0, precision: number | unitPrecisionMap = defaultPrecisionMap): string { | ||
38 | + if (isNaN(parseFloat(String(bytes))) || !isFinite(bytes)) { | ||
39 | + return '?'; | ||
40 | + } | ||
41 | + | ||
42 | + let unitIndex = 0; | ||
43 | + | ||
44 | + while (bytes >= 1024) { | ||
45 | + bytes /= 1024; | ||
46 | + unitIndex++; | ||
47 | + } | ||
48 | + | ||
49 | + const unitSymbol = this.units[unitIndex]; | ||
50 | + | ||
51 | + if (typeof precision === 'number') { | ||
52 | + return `${bytes.toFixed(+precision)} ${unitSymbol}`; | ||
53 | + } | ||
54 | + return `${bytes.toFixed(precision[unitSymbol])} ${unitSymbol}`; | ||
55 | + } | ||
56 | +} |
@@ -20,3 +20,4 @@ export * from './keyboard-shortcut.pipe'; | @@ -20,3 +20,4 @@ export * from './keyboard-shortcut.pipe'; | ||
20 | export * from './milliseconds-to-time-string.pipe'; | 20 | export * from './milliseconds-to-time-string.pipe'; |
21 | export * from './nospace.pipe'; | 21 | export * from './nospace.pipe'; |
22 | export * from './truncate.pipe'; | 22 | export * from './truncate.pipe'; |
23 | +export * from './file-size.pipe'; |
@@ -135,6 +135,7 @@ import { EntityGatewaySelectComponent } from '@shared/components/entity/entity-g | @@ -135,6 +135,7 @@ import { EntityGatewaySelectComponent } from '@shared/components/entity/entity-g | ||
135 | import { QueueTypeListComponent } from '@shared/components/queue/queue-type-list.component'; | 135 | import { QueueTypeListComponent } from '@shared/components/queue/queue-type-list.component'; |
136 | import { ContactComponent } from '@shared/components/contact.component'; | 136 | import { ContactComponent } from '@shared/components/contact.component'; |
137 | import { TimezoneSelectComponent } from '@shared/components/time/timezone-select.component'; | 137 | import { TimezoneSelectComponent } from '@shared/components/time/timezone-select.component'; |
138 | +import { FileSizePipe } from '@shared/pipe/file-size.pipe'; | ||
138 | 139 | ||
139 | @NgModule({ | 140 | @NgModule({ |
140 | providers: [ | 141 | providers: [ |
@@ -144,6 +145,7 @@ import { TimezoneSelectComponent } from '@shared/components/time/timezone-select | @@ -144,6 +145,7 @@ import { TimezoneSelectComponent } from '@shared/components/time/timezone-select | ||
144 | HighlightPipe, | 145 | HighlightPipe, |
145 | TruncatePipe, | 146 | TruncatePipe, |
146 | TbJsonPipe, | 147 | TbJsonPipe, |
148 | + FileSizePipe, | ||
147 | { | 149 | { |
148 | provide: FlowInjectionToken, | 150 | provide: FlowInjectionToken, |
149 | useValue: Flow | 151 | useValue: Flow |
@@ -217,6 +219,7 @@ import { TimezoneSelectComponent } from '@shared/components/time/timezone-select | @@ -217,6 +219,7 @@ import { TimezoneSelectComponent } from '@shared/components/time/timezone-select | ||
217 | HighlightPipe, | 219 | HighlightPipe, |
218 | TruncatePipe, | 220 | TruncatePipe, |
219 | TbJsonPipe, | 221 | TbJsonPipe, |
222 | + FileSizePipe, | ||
220 | KeyboardShortcutPipe, | 223 | KeyboardShortcutPipe, |
221 | TbJsonToStringDirective, | 224 | TbJsonToStringDirective, |
222 | JsonObjectEditDialogComponent, | 225 | JsonObjectEditDialogComponent, |
@@ -381,6 +384,7 @@ import { TimezoneSelectComponent } from '@shared/components/time/timezone-select | @@ -381,6 +384,7 @@ import { TimezoneSelectComponent } from '@shared/components/time/timezone-select | ||
381 | TruncatePipe, | 384 | TruncatePipe, |
382 | TbJsonPipe, | 385 | TbJsonPipe, |
383 | KeyboardShortcutPipe, | 386 | KeyboardShortcutPipe, |
387 | + FileSizePipe, | ||
384 | TranslateModule, | 388 | TranslateModule, |
385 | JsonObjectEditDialogComponent, | 389 | JsonObjectEditDialogComponent, |
386 | HistorySelectorComponent, | 390 | HistorySelectorComponent, |
@@ -2206,7 +2206,7 @@ | @@ -2206,7 +2206,7 @@ | ||
2206 | "timeseries": "Časové řady", | 2206 | "timeseries": "Časové řady", |
2207 | "search-data": "Vyhledat data", | 2207 | "search-data": "Vyhledat data", |
2208 | "no-data-found": "Žádná data nebyla nalezena", | 2208 | "no-data-found": "Žádná data nebyla nalezena", |
2209 | - "latest-values": "Poslední hodnoty", | 2209 | + "latest": "Poslední hodnoty", |
2210 | "rpc": "Ovládací widget", | 2210 | "rpc": "Ovládací widget", |
2211 | "alarm": "Widgety alarmu", | 2211 | "alarm": "Widgety alarmu", |
2212 | "static": "Statické widgety", | 2212 | "static": "Statické widgety", |
@@ -1451,7 +1451,7 @@ | @@ -1451,7 +1451,7 @@ | ||
1451 | "timeseries": "Zeitreihe", | 1451 | "timeseries": "Zeitreihe", |
1452 | "search-data": "Daten suchen", | 1452 | "search-data": "Daten suchen", |
1453 | "no-data-found": "Keine Daten gefunden", | 1453 | "no-data-found": "Keine Daten gefunden", |
1454 | - "latest-values": "Neueste Werte", | 1454 | + "latest": "Neueste Werte", |
1455 | "rpc": "Steuerungswidget", | 1455 | "rpc": "Steuerungswidget", |
1456 | "alarm": "Alarm-Widget", | 1456 | "alarm": "Alarm-Widget", |
1457 | "static": "Statisches Widget", | 1457 | "static": "Statisches Widget", |
@@ -2318,7 +2318,7 @@ | @@ -2318,7 +2318,7 @@ | ||
2318 | "timeseries": "Χρονική σειρά", | 2318 | "timeseries": "Χρονική σειρά", |
2319 | "search-data": "Αναζήτηση δεδομένων", | 2319 | "search-data": "Αναζήτηση δεδομένων", |
2320 | "no-data-found": "Δεν βρέθηκαν δεδομένα", | 2320 | "no-data-found": "Δεν βρέθηκαν δεδομένα", |
2321 | - "latest-values": "Τελευταίες αξίες", | 2321 | + "latest": "Τελευταίες αξίες", |
2322 | "rpc": "Έλεγχος Widget", | 2322 | "rpc": "Έλεγχος Widget", |
2323 | "alarm": "Alarm widget", | 2323 | "alarm": "Alarm widget", |
2324 | "static": "Στατικό widget", | 2324 | "static": "Στατικό widget", |
@@ -688,6 +688,8 @@ | @@ -688,6 +688,8 @@ | ||
688 | "background-size-mode": "Background size mode", | 688 | "background-size-mode": "Background size mode", |
689 | "no-image": "No image selected", | 689 | "no-image": "No image selected", |
690 | "drop-image": "Drop an image or click to select a file to upload.", | 690 | "drop-image": "Drop an image or click to select a file to upload.", |
691 | + "maximum-upload-file-size": "Maximum upload file size: {{ size }}", | ||
692 | + "cannot-upload-file": "Cannot upload file", | ||
691 | "settings": "Settings", | 693 | "settings": "Settings", |
692 | "columns-count": "Columns count", | 694 | "columns-count": "Columns count", |
693 | "columns-count-required": "Columns count is required.", | 695 | "columns-count-required": "Columns count is required.", |
@@ -2197,6 +2199,7 @@ | @@ -2197,6 +2199,7 @@ | ||
2197 | "widget": { | 2199 | "widget": { |
2198 | "widget-library": "Widgets Library", | 2200 | "widget-library": "Widgets Library", |
2199 | "widget-bundle": "Widgets Bundle", | 2201 | "widget-bundle": "Widgets Bundle", |
2202 | + "all-bundles": "All bundles", | ||
2200 | "select-widgets-bundle": "Select widgets bundle", | 2203 | "select-widgets-bundle": "Select widgets bundle", |
2201 | "management": "Widget management", | 2204 | "management": "Widget management", |
2202 | "editor": "Widget Editor", | 2205 | "editor": "Widget Editor", |
@@ -2209,7 +2212,7 @@ | @@ -2209,7 +2212,7 @@ | ||
2209 | "timeseries": "Time series", | 2212 | "timeseries": "Time series", |
2210 | "search-data": "Search data", | 2213 | "search-data": "Search data", |
2211 | "no-data-found": "No data found", | 2214 | "no-data-found": "No data found", |
2212 | - "latest-values": "Latest values", | 2215 | + "latest": "Latest values", |
2213 | "rpc": "Control widget", | 2216 | "rpc": "Control widget", |
2214 | "alarm": "Alarm widget", | 2217 | "alarm": "Alarm widget", |
2215 | "static": "Static widget", | 2218 | "static": "Static widget", |
@@ -2236,6 +2239,9 @@ | @@ -2236,6 +2239,9 @@ | ||
2236 | "css": "CSS", | 2239 | "css": "CSS", |
2237 | "settings-schema": "Settings schema", | 2240 | "settings-schema": "Settings schema", |
2238 | "datakey-settings-schema": "Data key settings schema", | 2241 | "datakey-settings-schema": "Data key settings schema", |
2242 | + "widget-settings": "Widget settings", | ||
2243 | + "description": "Description", | ||
2244 | + "image-preview": "Image preview", | ||
2239 | "javascript": "Javascript", | 2245 | "javascript": "Javascript", |
2240 | "js": "JS", | 2246 | "js": "JS", |
2241 | "remove-widget-type-title": "Are you sure you want to remove the widget type '{{widgetName}}'?", | 2247 | "remove-widget-type-title": "Are you sure you want to remove the widget type '{{widgetName}}'?", |
@@ -2278,6 +2284,8 @@ | @@ -2278,6 +2284,8 @@ | ||
2278 | "delete": "Delete widgets bundle", | 2284 | "delete": "Delete widgets bundle", |
2279 | "title": "Title", | 2285 | "title": "Title", |
2280 | "title-required": "Title is required.", | 2286 | "title-required": "Title is required.", |
2287 | + "description": "Description", | ||
2288 | + "image-preview": "Image preview", | ||
2281 | "add-widgets-bundle-text": "Add new widgets bundle", | 2289 | "add-widgets-bundle-text": "Add new widgets bundle", |
2282 | "no-widgets-bundles-text": "No widgets bundles found", | 2290 | "no-widgets-bundles-text": "No widgets bundles found", |
2283 | "empty": "Widgets bundle is empty", | 2291 | "empty": "Widgets bundle is empty", |
@@ -2206,7 +2206,7 @@ | @@ -2206,7 +2206,7 @@ | ||
2206 | "timeseries": "Series de tiempo", | 2206 | "timeseries": "Series de tiempo", |
2207 | "search-data": "Buscar datos", | 2207 | "search-data": "Buscar datos", |
2208 | "no-data-found": "No se han encontrado datos", | 2208 | "no-data-found": "No se han encontrado datos", |
2209 | - "latest-values": "Últimos valores", | 2209 | + "latest": "Últimos valores", |
2210 | "rpc": "Widget de control", | 2210 | "rpc": "Widget de control", |
2211 | "alarm": "Widget de Alarma", | 2211 | "alarm": "Widget de Alarma", |
2212 | "static": "Widget estático", | 2212 | "static": "Widget estático", |
@@ -1420,7 +1420,7 @@ | @@ -1420,7 +1420,7 @@ | ||
1420 | "timeseries": "سري هاي زماني", | 1420 | "timeseries": "سري هاي زماني", |
1421 | "search-data": "جستجوي داده", | 1421 | "search-data": "جستجوي داده", |
1422 | "no-data-found": "هيچ داده اي يافت نشد", | 1422 | "no-data-found": "هيچ داده اي يافت نشد", |
1423 | - "latest-values": "آخرين مقادير", | 1423 | + "latest": "آخرين مقادير", |
1424 | "rpc": "ويجت کنترل", | 1424 | "rpc": "ويجت کنترل", |
1425 | "alarm": "ويجت هشدار", | 1425 | "alarm": "ويجت هشدار", |
1426 | "static": "ويجت ايستا", | 1426 | "static": "ويجت ايستا", |
@@ -1494,7 +1494,7 @@ | @@ -1494,7 +1494,7 @@ | ||
1494 | "export": "Exporter widget", | 1494 | "export": "Exporter widget", |
1495 | "html": "HTML", | 1495 | "html": "HTML", |
1496 | "javascript": "Javascript", | 1496 | "javascript": "Javascript", |
1497 | - "latest-values": "Dernières valeurs", | 1497 | + "latest": "Dernières valeurs", |
1498 | "management": "Gestion des widgets", | 1498 | "management": "Gestion des widgets", |
1499 | "missing-widget-title-error": "Le titre du widget doit être spécifié!", | 1499 | "missing-widget-title-error": "Le titre du widget doit être spécifié!", |
1500 | "no-data-found": "Aucune donnée trouvée", | 1500 | "no-data-found": "Aucune donnée trouvée", |
@@ -1463,7 +1463,7 @@ | @@ -1463,7 +1463,7 @@ | ||
1463 | "timeseries": "Time series", | 1463 | "timeseries": "Time series", |
1464 | "search-data": "Cerca dati", | 1464 | "search-data": "Cerca dati", |
1465 | "no-data-found": "Nessun dato trovato", | 1465 | "no-data-found": "Nessun dato trovato", |
1466 | - "latest-values": "Ultimi valori", | 1466 | + "latest": "Ultimi valori", |
1467 | "rpc": "Control widget", | 1467 | "rpc": "Control widget", |
1468 | "alarm": "Alarm widget", | 1468 | "alarm": "Alarm widget", |
1469 | "static": "Static widget", | 1469 | "static": "Static widget", |
@@ -1305,7 +1305,7 @@ | @@ -1305,7 +1305,7 @@ | ||
1305 | "timeseries": "時系列", | 1305 | "timeseries": "時系列", |
1306 | "search-data": "検索データ", | 1306 | "search-data": "検索データ", |
1307 | "no-data-found": "何もデータが見つかりませんでした", | 1307 | "no-data-found": "何もデータが見つかりませんでした", |
1308 | - "latest-values": "最新の値", | 1308 | + "latest": "最新の値", |
1309 | "rpc": "コントロールウィジェット", | 1309 | "rpc": "コントロールウィジェット", |
1310 | "alarm": "アラームウィジェット", | 1310 | "alarm": "アラームウィジェット", |
1311 | "static": "静的ウィジェット", | 1311 | "static": "静的ウィジェット", |
@@ -1551,7 +1551,7 @@ | @@ -1551,7 +1551,7 @@ | ||
1551 | "timeseries": "მონაცემთა სერია", | 1551 | "timeseries": "მონაცემთა სერია", |
1552 | "search-data": "საძიებო მონაცემები", | 1552 | "search-data": "საძიებო მონაცემები", |
1553 | "no-data-found": "მონაცემი ვერ მოიძებნა", | 1553 | "no-data-found": "მონაცემი ვერ მოიძებნა", |
1554 | - "latest-values": "უახლესი მნიშვნელობები", | 1554 | + "latest": "უახლესი მნიშვნელობები", |
1555 | "rpc": "ვიჯეტის კონტროლი", | 1555 | "rpc": "ვიჯეტის კონტროლი", |
1556 | "alarm": "ვიჯეტის განგაში", | 1556 | "alarm": "ვიჯეტის განგაში", |
1557 | "static": "სტატიკური ვიჯეტი", | 1557 | "static": "სტატიკური ვიჯეტი", |
@@ -2202,7 +2202,7 @@ | @@ -2202,7 +2202,7 @@ | ||
2202 | "timeseries": "시계열", | 2202 | "timeseries": "시계열", |
2203 | "search-data": "데이터 검색", | 2203 | "search-data": "데이터 검색", |
2204 | "no-data-found": "아무 데이터도 없습니다", | 2204 | "no-data-found": "아무 데이터도 없습니다", |
2205 | - "latest-values": "최근 값", | 2205 | + "latest": "최근 값", |
2206 | "rpc": "컨트롤 위젯", | 2206 | "rpc": "컨트롤 위젯", |
2207 | "alarm": "알람 위젯", | 2207 | "alarm": "알람 위젯", |
2208 | "static": "상태 위젯", | 2208 | "static": "상태 위젯", |
@@ -1468,7 +1468,7 @@ | @@ -1468,7 +1468,7 @@ | ||
1468 | "timeseries": "Laika sērijas", | 1468 | "timeseries": "Laika sērijas", |
1469 | "search-data": "Meklēt datus", | 1469 | "search-data": "Meklēt datus", |
1470 | "no-data-found": "Nav datu atrasti", | 1470 | "no-data-found": "Nav datu atrasti", |
1471 | - "latest-values": "Pedējās vērtības", | 1471 | + "latest": "Pedējās vērtības", |
1472 | "rpc": "Kontroles logrīks", | 1472 | "rpc": "Kontroles logrīks", |
1473 | "alarm": "Trauksmes logrīks", | 1473 | "alarm": "Trauksmes logrīks", |
1474 | "static": "Statiskais logrīks", | 1474 | "static": "Statiskais logrīks", |
@@ -1777,7 +1777,7 @@ | @@ -1777,7 +1777,7 @@ | ||
1777 | "timeseries": "Intervalos de tempo", | 1777 | "timeseries": "Intervalos de tempo", |
1778 | "search-data": "Pesquisar dados", | 1778 | "search-data": "Pesquisar dados", |
1779 | "no-data-found": "Nenhum dado encontrado", | 1779 | "no-data-found": "Nenhum dado encontrado", |
1780 | - "latest-values": "Últimos valores", | 1780 | + "latest": "Últimos valores", |
1781 | "rpc": "Widget de controle", | 1781 | "rpc": "Widget de controle", |
1782 | "alarm": "Widget de alarme", | 1782 | "alarm": "Widget de alarme", |
1783 | "static": "Widget estático", | 1783 | "static": "Widget estático", |
@@ -1535,7 +1535,7 @@ | @@ -1535,7 +1535,7 @@ | ||
1535 | "timeseries": "Serii Temporale", | 1535 | "timeseries": "Serii Temporale", |
1536 | "search-data": "Caută Date", | 1536 | "search-data": "Caută Date", |
1537 | "no-data-found": "Nu Au Fost Găsite Date", | 1537 | "no-data-found": "Nu Au Fost Găsite Date", |
1538 | - "latest-values": "Ultimele Valori", | 1538 | + "latest": "Ultimele Valori", |
1539 | "rpc": "Widget Control ", | 1539 | "rpc": "Widget Control ", |
1540 | "alarm": "Widget Alarmă", | 1540 | "alarm": "Widget Alarmă", |
1541 | "static": "Widget Static", | 1541 | "static": "Widget Static", |
@@ -1541,7 +1541,7 @@ | @@ -1541,7 +1541,7 @@ | ||
1541 | "timeseries": "Телеметрия", | 1541 | "timeseries": "Телеметрия", |
1542 | "search-data": "Поиск данных", | 1542 | "search-data": "Поиск данных", |
1543 | "no-data-found": "Данные не найдено", | 1543 | "no-data-found": "Данные не найдено", |
1544 | - "latest-values": "Последние значения", | 1544 | + "latest": "Последние значения", |
1545 | "rpc": "Управляющий виджет", | 1545 | "rpc": "Управляющий виджет", |
1546 | "alarm": "Виджет оповещений", | 1546 | "alarm": "Виджет оповещений", |
1547 | "static": "Статический виджет", | 1547 | "static": "Статический виджет", |
@@ -2202,7 +2202,7 @@ | @@ -2202,7 +2202,7 @@ | ||
2202 | "timeseries": "Časovne serije", | 2202 | "timeseries": "Časovne serije", |
2203 | "search-data": "Iskanje podatkov", | 2203 | "search-data": "Iskanje podatkov", |
2204 | "no-data-found": "Podatkov ni mogoče najti", | 2204 | "no-data-found": "Podatkov ni mogoče najti", |
2205 | - "latest-values": "Najnovejše vrednosti", | 2205 | + "latest": "Najnovejše vrednosti", |
2206 | "rpc": "Nadzorni pripomoček", | 2206 | "rpc": "Nadzorni pripomoček", |
2207 | "alarm": "Pripomoček za alarm", | 2207 | "alarm": "Pripomoček za alarm", |
2208 | "static": "Statični pripomoček", | 2208 | "static": "Statični pripomoček", |
@@ -1464,7 +1464,7 @@ | @@ -1464,7 +1464,7 @@ | ||
1464 | "timeseries": "Zaman serisi", | 1464 | "timeseries": "Zaman serisi", |
1465 | "search-data": "Arama verileri", | 1465 | "search-data": "Arama verileri", |
1466 | "no-data-found": "Veri bulunamadı", | 1466 | "no-data-found": "Veri bulunamadı", |
1467 | - "latest-values": "Son değerler", | 1467 | + "latest": "Son değerler", |
1468 | "rpc": "Kontrol göstergesi", | 1468 | "rpc": "Kontrol göstergesi", |
1469 | "alarm": "Alarm göstergesi", | 1469 | "alarm": "Alarm göstergesi", |
1470 | "static": "Statik gösterge", | 1470 | "static": "Statik gösterge", |
@@ -2111,7 +2111,7 @@ | @@ -2111,7 +2111,7 @@ | ||
2111 | "timeseries": "Телеметрія", | 2111 | "timeseries": "Телеметрія", |
2112 | "search-data": "Пошук даних", | 2112 | "search-data": "Пошук даних", |
2113 | "no-data-found": "Даних не знайдено", | 2113 | "no-data-found": "Даних не знайдено", |
2114 | - "latest-values": "Останні значення", | 2114 | + "latest": "Останні значення", |
2115 | "rpc": "Керуючий віджет", | 2115 | "rpc": "Керуючий віджет", |
2116 | "alarm": "Віджет сигнала тривоги", | 2116 | "alarm": "Віджет сигнала тривоги", |
2117 | "static": "Статичний віджет", | 2117 | "static": "Статичний віджет", |
@@ -2323,7 +2323,7 @@ | @@ -2323,7 +2323,7 @@ | ||
2323 | "html": "HTML", | 2323 | "html": "HTML", |
2324 | "javascript": "Javascript", | 2324 | "javascript": "Javascript", |
2325 | "js": "JS", | 2325 | "js": "JS", |
2326 | - "latest-values": "最新值", | 2326 | + "latest": "最新值", |
2327 | "management": "管理部件", | 2327 | "management": "管理部件", |
2328 | "missing-widget-title-error": "部件标题必须指定!", | 2328 | "missing-widget-title-error": "部件标题必须指定!", |
2329 | "no-data": "小部件上没有要显示的数据", | 2329 | "no-data": "小部件上没有要显示的数据", |
@@ -2500,4 +2500,4 @@ | @@ -2500,4 +2500,4 @@ | ||
2500 | "value": "价值" | 2500 | "value": "价值" |
2501 | } | 2501 | } |
2502 | } | 2502 | } |
2503 | -} | ||
2503 | +} |
@@ -1396,7 +1396,7 @@ | @@ -1396,7 +1396,7 @@ | ||
1396 | "timeseries": "時間序列", | 1396 | "timeseries": "時間序列", |
1397 | "search-data": "搜尋資料", | 1397 | "search-data": "搜尋資料", |
1398 | "no-data-found": "沒有找到資料", | 1398 | "no-data-found": "沒有找到資料", |
1399 | - "latest-values": "最新值", | 1399 | + "latest": "最新值", |
1400 | "rpc": "控件部件", | 1400 | "rpc": "控件部件", |
1401 | "alarm": "警告部件", | 1401 | "alarm": "警告部件", |
1402 | "static": "靜態部件", | 1402 | "static": "靜態部件", |