Commit 3ea5314495c857796198a9183e2a160562a2f87f
1 parent
84647a07
Home dashboard feature. Enable tooltips for flot chars in mobile mode. Disable w…
…idgets interaction in widget library and when adding widget to dashboard.
Showing
51 changed files
with
1182 additions
and
84 deletions
... | ... | @@ -26,22 +26,6 @@ |
26 | 26 | } |
27 | 27 | }, |
28 | 28 | { |
29 | - "alias": "basic_timeseries", | |
30 | - "name": "Timeseries - Flot", | |
31 | - "descriptor": { | |
32 | - "type": "timeseries", | |
33 | - "sizeX": 8, | |
34 | - "sizeY": 5, | |
35 | - "resources": [], | |
36 | - "templateHtml": "", | |
37 | - "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", | |
38 | - "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema('graph');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true, 'graph');\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n", | |
39 | - "settingsSchema": "{}", | |
40 | - "dataKeySettingsSchema": "{}", | |
41 | - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"legend\":{\"show\":true,\"position\":\"nw\",\"backgroundColor\":\"#f0f0f0\",\"backgroundOpacity\":0.85,\"labelBoxBorderColor\":\"rgba(1, 1, 1, 0.45)\"},\"decimals\":1,\"stack\":false,\"tooltipIndividual\":false},\"title\":\"Timeseries - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null}" | |
42 | - } | |
43 | - }, | |
44 | - { | |
45 | 29 | "alias": "doughnut_chart_js", |
46 | 30 | "name": "Doughnut - Chart.js", |
47 | 31 | "descriptor": { |
... | ... | @@ -71,7 +55,7 @@ |
71 | 55 | "resources": [], |
72 | 56 | "templateHtml": "", |
73 | 57 | "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.pie-label {\n font-size: 12px;\n font-family: 'Roboto';\n font-weight: bold;\n text-align: center;\n padding: 2px;\n color: white;\n}\n", |
74 | - "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'pie'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.pieSettingsSchema();\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.pieDatakeySettingsSchema();\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\nself.actionSources = function() {\n return {\n 'sliceClick': {\n name: 'widget-action.pie-slice-click',\n multiple: false\n }\n };\n}\n", | |
58 | + "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'pie'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.pieSettingsSchema();\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.pieDatakeySettingsSchema();\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\nself.actionSources = function() {\n return {\n 'sliceClick': {\n name: 'widget-action.pie-slice-click',\n multiple: false\n }\n };\n}\n", | |
75 | 59 | "settingsSchema": "{}\n", |
76 | 60 | "dataKeySettingsSchema": "{}\n", |
77 | 61 | "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"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\":\"#4caf50\",\"settings\":{},\"_hash\":0.6114638304362894,\"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\":\"#f44336\",\"settings\":{},\"_hash\":0.9955906536344441,\"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\":\"#ffc107\",\"settings\":{},\"_hash\":0.9430835931647599,\"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\":{\"radius\":1,\"fontColor\":\"#545454\",\"fontSize\":10,\"decimals\":1,\"legend\":{\"show\":true,\"position\":\"nw\",\"labelBoxBorderColor\":\"#CCCCCC\",\"backgroundColor\":\"#F0F0F0\",\"backgroundOpacity\":0.85},\"innerRadius\":0,\"showLabels\":true,\"showPercentages\":true,\"stroke\":{\"width\":5},\"tilt\":1,\"animatedPie\":false},\"title\":\"Pie - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" |
... | ... | @@ -138,8 +122,8 @@ |
138 | 122 | } |
139 | 123 | }, |
140 | 124 | { |
141 | - "alias": "timeseries_bars_flot", | |
142 | - "name": "Timeseries Bars - Flot", | |
125 | + "alias": "state_chart", | |
126 | + "name": "State Chart", | |
143 | 127 | "descriptor": { |
144 | 128 | "type": "timeseries", |
145 | 129 | "sizeX": 8, |
... | ... | @@ -147,15 +131,15 @@ |
147 | 131 | "resources": [], |
148 | 132 | "templateHtml": "", |
149 | 133 | "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", |
150 | - "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'bar'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema('bar');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(false, 'bar');\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n", | |
134 | + "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'state'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.typeParameters = function() {\n return {\n stateData: true\n };\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema('graph');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true, 'graph');\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n", | |
151 | 135 | "settingsSchema": "{}", |
152 | 136 | "dataKeySettingsSchema": "{}", |
153 | - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000},\"aggregation\":{\"limit\":200,\"type\":\"AVG\"}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"stack\":true,\"tooltipIndividual\":false,\"defaultBarWidth\":600},\"title\":\"Timeseries Bars - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{}}" | |
137 | + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 1\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false,\"axisPosition\":\"left\",\"showSeparateAxis\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"return Math.random() > 0.5 ? 1 : 0;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 2\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false,\"axisPosition\":\"left\"},\"_hash\":0.12775350966079668,\"funcBody\":\"return Math.random() <= 0.5 ? 1 : 0;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\",\"ticksFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"stack\":false,\"tooltipIndividual\":false,\"tooltipValueFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\",\"smoothLines\":false},\"title\":\"State Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{},\"legendConfig\":{\"direction\":\"column\",\",position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false}}" | |
154 | 138 | } |
155 | 139 | }, |
156 | 140 | { |
157 | - "alias": "state_chart", | |
158 | - "name": "State Chart", | |
141 | + "alias": "basic_timeseries", | |
142 | + "name": "Timeseries - Flot", | |
159 | 143 | "descriptor": { |
160 | 144 | "type": "timeseries", |
161 | 145 | "sizeX": 8, |
... | ... | @@ -163,11 +147,27 @@ |
163 | 147 | "resources": [], |
164 | 148 | "templateHtml": "", |
165 | 149 | "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", |
166 | - "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'state'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.typeParameters = function() {\n return {\n stateData: true\n };\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema('graph');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true, 'graph');\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n", | |
150 | + "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema('graph');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true, 'graph');\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n", | |
167 | 151 | "settingsSchema": "{}", |
168 | 152 | "dataKeySettingsSchema": "{}", |
169 | - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 1\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false,\"axisPosition\":\"left\",\"showSeparateAxis\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"return Math.random() > 0.5 ? 1 : 0;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 2\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false,\"axisPosition\":\"left\"},\"_hash\":0.12775350966079668,\"funcBody\":\"return Math.random() <= 0.5 ? 1 : 0;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\",\"ticksFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"stack\":false,\"tooltipIndividual\":false,\"tooltipValueFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\",\"smoothLines\":false},\"title\":\"State Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{},\"legendConfig\":{\"direction\":\"column\",\",position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false}}" | |
153 | + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"legend\":{\"show\":true,\"position\":\"nw\",\"backgroundColor\":\"#f0f0f0\",\"backgroundOpacity\":0.85,\"labelBoxBorderColor\":\"rgba(1, 1, 1, 0.45)\"},\"decimals\":1,\"stack\":false,\"tooltipIndividual\":false},\"title\":\"Timeseries - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null}" | |
154 | + } | |
155 | + }, | |
156 | + { | |
157 | + "alias": "timeseries_bars_flot", | |
158 | + "name": "Timeseries Bars - Flot", | |
159 | + "descriptor": { | |
160 | + "type": "timeseries", | |
161 | + "sizeX": 8, | |
162 | + "sizeY": 5, | |
163 | + "resources": [], | |
164 | + "templateHtml": "", | |
165 | + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", | |
166 | + "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'bar'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema('bar');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(false, 'bar');\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n", | |
167 | + "settingsSchema": "{}", | |
168 | + "dataKeySettingsSchema": "{}", | |
169 | + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000},\"aggregation\":{\"limit\":200,\"type\":\"AVG\"}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"stack\":true,\"tooltipIndividual\":false,\"defaultBarWidth\":600},\"title\":\"Timeseries Bars - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{}}" | |
170 | 170 | } |
171 | 171 | } |
172 | 172 | ] |
173 | -} | |
173 | +} | |
\ No newline at end of file | ... | ... |
1 | +{ | |
2 | + "widgetsBundle": { | |
3 | + "alias": "navigation_widgets", | |
4 | + "title": "Navigation widgets", | |
5 | + "image": null | |
6 | + }, | |
7 | + "widgetTypes": [ | |
8 | + { | |
9 | + "alias": "navigation_cards", | |
10 | + "name": "Navigation cards", | |
11 | + "descriptor": { | |
12 | + "type": "static", | |
13 | + "sizeX": 7, | |
14 | + "sizeY": 6, | |
15 | + "resources": [], | |
16 | + "templateHtml": "<tb-navigation-cards-widget [ctx]=\"ctx\"></tb-navigation-cards-widget>", | |
17 | + "templateCss": "/*#widget-container {\n overflow-y: auto;\n box-sizing: content-box !important;\n cursor: auto;\n}*/\n\n#widget-container #container {\n overflow-y: auto;\n box-sizing: content-box;\n cursor: auto;\n}", | |
18 | + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.navigationCardsWidget.resize();\n}\n\nself.onResize = function() {\n self.ctx.$scope.navigationCardsWidget.resize();\n}\n\nself.onDestroy = function() {\n}\n", | |
19 | + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"filterType\": {\n \"title\": \"Filter type\",\n \"type\": \"string\",\n \"default\": \"all\"\n },\n \"filter\": {\n \"title\": \"Items\",\n \"type\": \"array\"\n }\n },\n \"required\": []\n },\n \"form\": [\n {\n \"key\": \"filterType\",\n \"type\": \"radios\",\n \"direction\": \"row\",\n \"titleMap\": [\n {\n \"value\": \"all\",\n \"name\": \"All items\"\n },\n {\n \"value\": \"include\",\n \"name\": \"Include items\"\n },\n {\n \"value\": \"exclude\",\n \"name\": \"Exclude items\"\n }\n ]\n },\n {\n \"key\": \"filter\",\n \"type\": \"rc-select\",\n \"condition\": \"model.filterType !== 'all'\",\n \"tags\": true,\n \"placeholder\": \"Enter urls to filter\",\n \"items\": [{\"value\": \"/devices\", \"label\": \"/devices\"}, {\"value\": \"/assets\", \"label\": \"/assets\"}, {\"value\": \"/deviceProfies\", \"label\": \"/deviceProfies\"}]\n }\n ]\n}\n", | |
20 | + "dataKeySettingsSchema": "{}\n", | |
21 | + "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(255,255,255,0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"filterType\":\"all\"},\"title\":\"Navigation cards\",\"dropShadow\":false,\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false}" | |
22 | + } | |
23 | + }, | |
24 | + { | |
25 | + "alias": "navigation_card", | |
26 | + "name": "Navigation card", | |
27 | + "descriptor": { | |
28 | + "type": "static", | |
29 | + "sizeX": 2.5, | |
30 | + "sizeY": 2, | |
31 | + "resources": [], | |
32 | + "templateHtml": "<tb-navigation-card-widget [ctx]=\"ctx\"></tb-navigation-card-widget>", | |
33 | + "templateCss": "", | |
34 | + "controllerScript": "self.onInit = function() {\n\n}\n\n\nself.onDestroy = function() {\n}\n", | |
35 | + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"name\": {\n \"title\": \"Title\",\n \"type\": \"string\",\n \"default\": \"{i18n:device.devices}\"\n },\n \"icon\": {\n \"title\": \"icon\",\n \"type\": \"string\",\n \"default\": \"devices_other\"\n },\n \"path\": {\n \"title\": \"Navigation path\",\n \"type\": \"string\",\n \"default\": \"/devices\"\n }\n },\n \"required\": [\"name\", \"icon\", \"path\"]\n },\n \"form\": [\n \"name\",\n {\n \"key\": \"icon\",\n \"type\": \"icon\"\n },\n \"path\"\n ]\n}\n", | |
36 | + "dataKeySettingsSchema": "{}\n", | |
37 | + "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(255,255,255,0)\",\"color\":\"rgba(255,255,255,0.87)\",\"padding\":\"8px\",\"settings\":{\"name\":\"{i18n:device.devices}\",\"icon\":\"devices_other\",\"path\":\"/devices\"},\"title\":\"Navigation card\",\"dropShadow\":false,\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false}" | |
38 | + } | |
39 | + } | |
40 | + ] | |
41 | +} | |
\ No newline at end of file | ... | ... |
... | ... | @@ -15,6 +15,8 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.controller; |
17 | 17 | |
18 | +import com.fasterxml.jackson.databind.JsonNode; | |
19 | +import com.fasterxml.jackson.databind.node.ObjectNode; | |
18 | 20 | import org.springframework.beans.factory.annotation.Value; |
19 | 21 | import org.springframework.http.HttpStatus; |
20 | 22 | import org.springframework.security.access.prepost.PreAuthorize; |
... | ... | @@ -30,7 +32,11 @@ import org.thingsboard.server.common.data.Customer; |
30 | 32 | import org.thingsboard.server.common.data.Dashboard; |
31 | 33 | import org.thingsboard.server.common.data.DashboardInfo; |
32 | 34 | import org.thingsboard.server.common.data.EntityType; |
35 | +import org.thingsboard.server.common.data.HomeDashboard; | |
36 | +import org.thingsboard.server.common.data.HomeDashboardInfo; | |
33 | 37 | import org.thingsboard.server.common.data.ShortCustomerInfo; |
38 | +import org.thingsboard.server.common.data.Tenant; | |
39 | +import org.thingsboard.server.common.data.User; | |
34 | 40 | import org.thingsboard.server.common.data.audit.ActionType; |
35 | 41 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
36 | 42 | import org.thingsboard.server.common.data.id.CustomerId; |
... | ... | @@ -39,7 +45,9 @@ import org.thingsboard.server.common.data.id.TenantId; |
39 | 45 | import org.thingsboard.server.common.data.page.PageData; |
40 | 46 | import org.thingsboard.server.common.data.page.PageLink; |
41 | 47 | import org.thingsboard.server.common.data.page.TimePageLink; |
48 | +import org.thingsboard.server.dao.util.mapping.JacksonUtil; | |
42 | 49 | import org.thingsboard.server.queue.util.TbCoreComponent; |
50 | +import org.thingsboard.server.service.security.model.SecurityUser; | |
43 | 51 | import org.thingsboard.server.service.security.permission.Operation; |
44 | 52 | import org.thingsboard.server.service.security.permission.Resource; |
45 | 53 | |
... | ... | @@ -53,6 +61,9 @@ public class DashboardController extends BaseController { |
53 | 61 | |
54 | 62 | public static final String DASHBOARD_ID = "dashboardId"; |
55 | 63 | |
64 | + private static final String HOME_DASHBOARD_ID = "homeDashboardId"; | |
65 | + private static final String HOME_DASHBOARD_HIDE_TOOLBAR = "homeDashboardHideToolbar"; | |
66 | + | |
56 | 67 | @Value("${dashboard.max_datapoints_limit}") |
57 | 68 | private long maxDatapointsLimit; |
58 | 69 | |
... | ... | @@ -472,4 +483,100 @@ public class DashboardController extends BaseController { |
472 | 483 | throw handleException(e); |
473 | 484 | } |
474 | 485 | } |
486 | + | |
487 | + @PreAuthorize("isAuthenticated()") | |
488 | + @RequestMapping(value = "/dashboard/home", method = RequestMethod.GET) | |
489 | + @ResponseBody | |
490 | + public HomeDashboard getHomeDashboard() throws ThingsboardException { | |
491 | + try { | |
492 | + SecurityUser securityUser = getCurrentUser(); | |
493 | + if (securityUser.isSystemAdmin()) { | |
494 | + return null; | |
495 | + } | |
496 | + User user = userService.findUserById(securityUser.getTenantId(), securityUser.getId()); | |
497 | + JsonNode additionalInfo = user.getAdditionalInfo(); | |
498 | + HomeDashboard homeDashboard; | |
499 | + homeDashboard = extractHomeDashboardFromAdditionalInfo(additionalInfo); | |
500 | + if (homeDashboard == null) { | |
501 | + if (securityUser.isCustomerUser()) { | |
502 | + Customer customer = customerService.findCustomerById(securityUser.getTenantId(), securityUser.getCustomerId()); | |
503 | + additionalInfo = customer.getAdditionalInfo(); | |
504 | + homeDashboard = extractHomeDashboardFromAdditionalInfo(additionalInfo); | |
505 | + } | |
506 | + if (homeDashboard == null) { | |
507 | + Tenant tenant = tenantService.findTenantById(securityUser.getTenantId()); | |
508 | + additionalInfo = tenant.getAdditionalInfo(); | |
509 | + homeDashboard = extractHomeDashboardFromAdditionalInfo(additionalInfo); | |
510 | + } | |
511 | + } | |
512 | + return homeDashboard; | |
513 | + } catch (Exception e) { | |
514 | + throw handleException(e); | |
515 | + } | |
516 | + } | |
517 | + | |
518 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
519 | + @RequestMapping(value = "/tenant/dashboard/home/info", method = RequestMethod.GET) | |
520 | + @ResponseBody | |
521 | + public HomeDashboardInfo getTenantHomeDashboardInfo() throws ThingsboardException { | |
522 | + try { | |
523 | + Tenant tenant = tenantService.findTenantById(getTenantId()); | |
524 | + JsonNode additionalInfo = tenant.getAdditionalInfo(); | |
525 | + DashboardId dashboardId = null; | |
526 | + boolean hideDashboardToolbar = true; | |
527 | + if (additionalInfo != null && additionalInfo.has(HOME_DASHBOARD_ID)) { | |
528 | + String strDashboardId = additionalInfo.get(HOME_DASHBOARD_ID).asText(); | |
529 | + dashboardId = new DashboardId(toUUID(strDashboardId)); | |
530 | + if (additionalInfo.has(HOME_DASHBOARD_HIDE_TOOLBAR)) { | |
531 | + hideDashboardToolbar = additionalInfo.get(HOME_DASHBOARD_HIDE_TOOLBAR).asBoolean(); | |
532 | + } | |
533 | + } | |
534 | + return new HomeDashboardInfo(dashboardId, hideDashboardToolbar); | |
535 | + } catch (Exception e) { | |
536 | + throw handleException(e); | |
537 | + } | |
538 | + } | |
539 | + | |
540 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
541 | + @RequestMapping(value = "/tenant/dashboard/home/info", method = RequestMethod.POST) | |
542 | + @ResponseStatus(value = HttpStatus.OK) | |
543 | + public void setTenantHomeDashboardInfo(@RequestBody HomeDashboardInfo homeDashboardInfo) throws ThingsboardException { | |
544 | + try { | |
545 | + if (homeDashboardInfo.getDashboardId() != null) { | |
546 | + checkDashboardId(homeDashboardInfo.getDashboardId(), Operation.READ); | |
547 | + } | |
548 | + Tenant tenant = tenantService.findTenantById(getTenantId()); | |
549 | + JsonNode additionalInfo = tenant.getAdditionalInfo(); | |
550 | + if (additionalInfo == null || !(additionalInfo instanceof ObjectNode)) { | |
551 | + additionalInfo = JacksonUtil.OBJECT_MAPPER.createObjectNode(); | |
552 | + } | |
553 | + if (homeDashboardInfo.getDashboardId() != null) { | |
554 | + ((ObjectNode) additionalInfo).put(HOME_DASHBOARD_ID, homeDashboardInfo.getDashboardId().getId().toString()); | |
555 | + ((ObjectNode) additionalInfo).put(HOME_DASHBOARD_HIDE_TOOLBAR, homeDashboardInfo.isHideDashboardToolbar()); | |
556 | + } else { | |
557 | + ((ObjectNode) additionalInfo).remove(HOME_DASHBOARD_ID); | |
558 | + ((ObjectNode) additionalInfo).remove(HOME_DASHBOARD_HIDE_TOOLBAR); | |
559 | + } | |
560 | + tenant.setAdditionalInfo(additionalInfo); | |
561 | + tenantService.saveTenant(tenant); | |
562 | + } catch (Exception e) { | |
563 | + throw handleException(e); | |
564 | + } | |
565 | + } | |
566 | + | |
567 | + private HomeDashboard extractHomeDashboardFromAdditionalInfo(JsonNode additionalInfo) { | |
568 | + try { | |
569 | + if (additionalInfo != null && additionalInfo.has(HOME_DASHBOARD_ID)) { | |
570 | + String strDashboardId = additionalInfo.get(HOME_DASHBOARD_ID).asText(); | |
571 | + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); | |
572 | + Dashboard dashboard = checkDashboardId(dashboardId, Operation.READ); | |
573 | + boolean hideDashboardToolbar = true; | |
574 | + if (additionalInfo.has(HOME_DASHBOARD_HIDE_TOOLBAR)) { | |
575 | + hideDashboardToolbar = additionalInfo.get(HOME_DASHBOARD_HIDE_TOOLBAR).asBoolean(); | |
576 | + } | |
577 | + return new HomeDashboard(dashboard, hideDashboardToolbar); | |
578 | + } | |
579 | + } catch (Exception e) {} | |
580 | + return null; | |
581 | + } | |
475 | 582 | } | ... | ... |
... | ... | @@ -185,6 +185,8 @@ public class ThingsboardInstallService { |
185 | 185 | case "3.2.0": |
186 | 186 | log.info("Upgrading ThingsBoard from version 3.2.0 to 3.2.1 ..."); |
187 | 187 | databaseEntitiesUpgradeService.upgradeDatabase("3.2.0"); |
188 | + case "3.2.1": | |
189 | + log.info("Upgrading ThingsBoard from version 3.2.1 to 3.3.0 ..."); | |
188 | 190 | log.info("Updating system data..."); |
189 | 191 | systemDataLoaderService.updateSystemWidgets(); |
190 | 192 | break; | ... | ... |
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
... | ... | @@ -438,6 +438,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { |
438 | 438 | this.deleteSystemWidgetBundle("input_widgets"); |
439 | 439 | this.deleteSystemWidgetBundle("date"); |
440 | 440 | this.deleteSystemWidgetBundle("entity_admin_widgets"); |
441 | + this.deleteSystemWidgetBundle("navigation_widgets"); | |
441 | 442 | installScripts.loadSystemWidgets(); |
442 | 443 | } |
443 | 444 | ... | ... |
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 | +package org.thingsboard.server.common.data; | |
17 | + | |
18 | +import lombok.Data; | |
19 | + | |
20 | +@Data | |
21 | +public class HomeDashboard extends Dashboard { | |
22 | + | |
23 | + private boolean hideDashboardToolbar; | |
24 | + | |
25 | + public HomeDashboard(Dashboard dashboard, boolean hideDashboardToolbar) { | |
26 | + super(dashboard); | |
27 | + this.hideDashboardToolbar = hideDashboardToolbar; | |
28 | + } | |
29 | + | |
30 | +} | ... | ... |
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 | +package org.thingsboard.server.common.data; | |
17 | + | |
18 | +import lombok.AllArgsConstructor; | |
19 | +import lombok.Data; | |
20 | +import org.thingsboard.server.common.data.id.DashboardId; | |
21 | + | |
22 | +@Data | |
23 | +@AllArgsConstructor | |
24 | +public class HomeDashboardInfo { | |
25 | + private DashboardId dashboardId; | |
26 | + private boolean hideDashboardToolbar; | |
27 | +} | ... | ... |
... | ... | @@ -53,8 +53,8 @@ export class AliasController implements IAliasController { |
53 | 53 | private stateControllerHolder: StateControllerHolder, |
54 | 54 | private origEntityAliases: EntityAliases, |
55 | 55 | private origFilters: Filters) { |
56 | - this.entityAliases = deepClone(this.origEntityAliases); | |
57 | - this.filters = deepClone(this.origFilters); | |
56 | + this.entityAliases = deepClone(this.origEntityAliases) || {}; | |
57 | + this.filters = deepClone(this.origFilters) || {}; | |
58 | 58 | this.userFilters = {}; |
59 | 59 | } |
60 | 60 | ... | ... |
... | ... | @@ -15,7 +15,7 @@ |
15 | 15 | /// |
16 | 16 | |
17 | 17 | import { Injectable, NgZone } from '@angular/core'; |
18 | -import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, RouterStateSnapshot } from '@angular/router'; | |
18 | +import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, Router, RouterStateSnapshot } from '@angular/router'; | |
19 | 19 | import { AuthService } from '../auth/auth.service'; |
20 | 20 | import { select, Store } from '@ngrx/store'; |
21 | 21 | import { AppState } from '../core.state'; |
... | ... | @@ -28,6 +28,7 @@ import { Authority } from '@shared/models/authority.enum'; |
28 | 28 | import { DialogService } from '@core/services/dialog.service'; |
29 | 29 | import { TranslateService } from '@ngx-translate/core'; |
30 | 30 | import { UtilsService } from '@core/services/utils.service'; |
31 | +import { isObject } from '@core/utils'; | |
31 | 32 | |
32 | 33 | @Injectable({ |
33 | 34 | providedIn: 'root' |
... | ... | @@ -35,6 +36,7 @@ import { UtilsService } from '@core/services/utils.service'; |
35 | 36 | export class AuthGuard implements CanActivate, CanActivateChild { |
36 | 37 | |
37 | 38 | constructor(private store: Store<AppState>, |
39 | + private router: Router, | |
38 | 40 | private authService: AuthService, |
39 | 41 | private dialogService: DialogService, |
40 | 42 | private utils: UtilsService, |
... | ... | @@ -115,6 +117,14 @@ export class AuthGuard implements CanActivate, CanActivateChild { |
115 | 117 | if (data.auth && data.auth.indexOf(authority) === -1) { |
116 | 118 | this.dialogService.forbidden(); |
117 | 119 | return of(false); |
120 | + } else if (data.redirectTo) { | |
121 | + let redirect; | |
122 | + if (isObject(data.redirectTo)) { | |
123 | + redirect = data.redirectTo[authority]; | |
124 | + } else { | |
125 | + redirect = data.redirectTo; | |
126 | + } | |
127 | + return of(this.router.parseUrl(redirect)); | |
118 | 128 | } else { |
119 | 129 | return of(true); |
120 | 130 | } | ... | ... |
... | ... | @@ -20,7 +20,7 @@ import { Observable } from 'rxjs'; |
20 | 20 | 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 | -import { Dashboard, DashboardInfo } from '@shared/models/dashboard.models'; | |
23 | +import { Dashboard, DashboardInfo, HomeDashboard, HomeDashboardInfo } from '@shared/models/dashboard.models'; | |
24 | 24 | import { WINDOW } from '@core/services/window.service'; |
25 | 25 | import { NavigationEnd, Router } from '@angular/router'; |
26 | 26 | import { filter, map, publishReplay, refCount } from 'rxjs/operators'; |
... | ... | @@ -122,6 +122,19 @@ export class DashboardService { |
122 | 122 | defaultHttpOptionsFromConfig(config)); |
123 | 123 | } |
124 | 124 | |
125 | + public getHomeDashboard(config?: RequestConfig): Observable<HomeDashboard> { | |
126 | + return this.http.get<HomeDashboard>('/api/dashboard/home', defaultHttpOptionsFromConfig(config)); | |
127 | + } | |
128 | + | |
129 | + public getTenantHomeDashboardInfo(config?: RequestConfig): Observable<HomeDashboardInfo> { | |
130 | + return this.http.get<HomeDashboardInfo>('/api/tenant/dashboard/home/info', defaultHttpOptionsFromConfig(config)); | |
131 | + } | |
132 | + | |
133 | + public setTenantHomeDashboardInfo(homeDashboardInfo: HomeDashboardInfo, config?: RequestConfig): Observable<any> { | |
134 | + return this.http.post<any>('/api/tenant/dashboard/home/info', homeDashboardInfo, | |
135 | + defaultHttpOptionsFromConfig(config)); | |
136 | + } | |
137 | + | |
125 | 138 | public getPublicDashboardLink(dashboard: DashboardInfo): string | null { |
126 | 139 | if (dashboard && dashboard.assignedCustomers && dashboard.assignedCustomers.length > 0) { |
127 | 140 | const publicCustomers = dashboard.assignedCustomers | ... | ... |
... | ... | @@ -284,6 +284,13 @@ export class MenuService { |
284 | 284 | }, |
285 | 285 | { |
286 | 286 | id: guid(), |
287 | + name: 'admin.home-settings', | |
288 | + type: 'link', | |
289 | + path: '/settings/home', | |
290 | + icon: 'settings_applications' | |
291 | + }, | |
292 | + { | |
293 | + id: guid(), | |
287 | 294 | name: 'audit-log.audit-logs', |
288 | 295 | type: 'link', |
289 | 296 | path: '/auditLogs', | ... | ... |
... | ... | @@ -23,6 +23,7 @@ |
23 | 23 | [widgetLayouts]="{}" |
24 | 24 | [isEdit]="false" |
25 | 25 | [isMobile]="true" |
26 | + [disableWidgetInteraction]="true" | |
26 | 27 | [isEditActionEnabled]="false" |
27 | 28 | [isExportActionEnabled]="false" |
28 | 29 | [isRemoveActionEnabled]="false" |
... | ... | @@ -34,6 +35,7 @@ |
34 | 35 | [widgetLayouts]="{}" |
35 | 36 | [isEdit]="false" |
36 | 37 | [isMobile]="true" |
38 | + [disableWidgetInteraction]="true" | |
37 | 39 | [isEditActionEnabled]="false" |
38 | 40 | [isExportActionEnabled]="false" |
39 | 41 | [isRemoveActionEnabled]="false" |
... | ... | @@ -45,6 +47,7 @@ |
45 | 47 | [widgetLayouts]="{}" |
46 | 48 | [isEdit]="false" |
47 | 49 | [isMobile]="true" |
50 | + [disableWidgetInteraction]="true" | |
48 | 51 | [isEditActionEnabled]="false" |
49 | 52 | [isExportActionEnabled]="false" |
50 | 53 | [isRemoveActionEnabled]="false" |
... | ... | @@ -56,6 +59,7 @@ |
56 | 59 | [widgetLayouts]="{}" |
57 | 60 | [isEdit]="false" |
58 | 61 | [isMobile]="true" |
62 | + [disableWidgetInteraction]="true" | |
59 | 63 | [isEditActionEnabled]="false" |
60 | 64 | [isExportActionEnabled]="false" |
61 | 65 | [isRemoveActionEnabled]="false" |
... | ... | @@ -67,6 +71,7 @@ |
67 | 71 | [widgetLayouts]="{}" |
68 | 72 | [isEdit]="false" |
69 | 73 | [isMobile]="true" |
74 | + [disableWidgetInteraction]="true" | |
70 | 75 | [isEditActionEnabled]="false" |
71 | 76 | [isExportActionEnabled]="false" |
72 | 77 | [isRemoveActionEnabled]="false" | ... | ... |
... | ... | @@ -53,6 +53,7 @@ |
53 | 53 | [mobileRowHeight]="layoutCtx.gridSettings.mobileRowHeight" |
54 | 54 | [isMobile]="isMobile" |
55 | 55 | [isMobileDisabled]="widgetEditMode" |
56 | + [disableWidgetInteraction]="isEdit" | |
56 | 57 | [isEditActionEnabled]="isEdit" |
57 | 58 | [isExportActionEnabled]="isEdit && !widgetEditMode" |
58 | 59 | [isRemoveActionEnabled]="isEdit && !widgetEditMode" | ... | ... |
... | ... | @@ -150,7 +150,7 @@ |
150 | 150 | </button> |
151 | 151 | </div> |
152 | 152 | </div> |
153 | - <div fxFlex fxLayout="column" class="tb-widget-content"> | |
153 | + <div fxFlex fxLayout="column" class="tb-widget-content" [ngClass]="{'tb-no-interaction': disableWidgetInteraction}"> | |
154 | 154 | <tb-widget fxFlex |
155 | 155 | #widgetComponent |
156 | 156 | [dashboardWidget]="widget" | ... | ... |
... | ... | @@ -114,6 +114,9 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo |
114 | 114 | isRemoveActionEnabled: boolean; |
115 | 115 | |
116 | 116 | @Input() |
117 | + disableWidgetInteraction = false; | |
118 | + | |
119 | + @Input() | |
117 | 120 | dashboardStyle: {[klass: string]: any}; |
118 | 121 | |
119 | 122 | @Input() | ... | ... |
... | ... | @@ -175,7 +175,7 @@ export class TbFlot { |
175 | 175 | autoHighlight: this.tooltipIndividual === true, |
176 | 176 | markings: [] |
177 | 177 | }, |
178 | - selection : { mode : ctx.isMobile ? null : 'x' }, | |
178 | + selection : { mode : 'x' }, | |
179 | 179 | legend : { |
180 | 180 | show: false |
181 | 181 | } |
... | ... | @@ -702,7 +702,7 @@ export class TbFlot { |
702 | 702 | } |
703 | 703 | |
704 | 704 | public checkMouseEvents() { |
705 | - const enabled = !this.ctx.isMobile && !this.ctx.isEdit; | |
705 | + const enabled = !this.ctx.isEdit; | |
706 | 706 | if (isUndefined(this.mouseEventsEnabled) || this.mouseEventsEnabled !== enabled) { |
707 | 707 | this.mouseEventsEnabled = enabled; |
708 | 708 | if (this.$element) { | ... | ... |
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016-2021 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<a mat-raised-button color="primary" class="tb-nav-button" href="{{settings.path}}" (click)="navigate($event, settings.path)"> | |
19 | + <mat-icon class="material-icons tb-mat-96">{{settings.icon}}</mat-icon> | |
20 | + <span>{{translatedName}}</span> | |
21 | +</a> | ... | ... |
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 | +:host { | |
18 | + width: 100%; | |
19 | + height: 100%; | |
20 | +} | |
21 | + | |
22 | +:host ::ng-deep { | |
23 | + .tb-nav-button { | |
24 | + width: 100%; | |
25 | + height: 100%; | |
26 | + &:hover { | |
27 | + border-bottom: none; | |
28 | + } | |
29 | + &:focus { | |
30 | + border-bottom: none; | |
31 | + } | |
32 | + .mat-button-wrapper { | |
33 | + width: 100%; | |
34 | + height: 100%; | |
35 | + display: flex; | |
36 | + flex-direction: column; | |
37 | + align-items: center; | |
38 | + mat-icon { | |
39 | + margin: auto; | |
40 | + } | |
41 | + span { | |
42 | + height: 18px; | |
43 | + min-height: 36px; | |
44 | + max-height: 36px; | |
45 | + padding: 0 0 20px 0; | |
46 | + margin: auto; | |
47 | + font-size: 18px; | |
48 | + font-weight: 400; | |
49 | + line-height: 18px; | |
50 | + white-space: normal; | |
51 | + } | |
52 | + } | |
53 | + &.mat-raised-button.mat-primary { | |
54 | + .mat-ripple-element { | |
55 | + opacity: 0.3; | |
56 | + background-color: rgba(255, 255, 255, 0.3); | |
57 | + } | |
58 | + } | |
59 | + } | |
60 | +} | ... | ... |
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 { PageComponent } from '@shared/components/page.component'; | |
18 | +import { Component, Input, NgZone, OnInit } from '@angular/core'; | |
19 | +import { WidgetContext } from '@home/models/widget-component.models'; | |
20 | +import { Store } from '@ngrx/store'; | |
21 | +import { AppState } from '@core/core.state'; | |
22 | +import { Router } from '@angular/router'; | |
23 | +import { UtilsService } from '@core/services/utils.service'; | |
24 | + | |
25 | +interface NavigationCardWidgetSettings { | |
26 | + name: string; | |
27 | + icon: string; | |
28 | + path: string; | |
29 | +} | |
30 | + | |
31 | +@Component({ | |
32 | + selector: 'tb-navigation-card-widget', | |
33 | + templateUrl: './navigation-card-widget.component.html', | |
34 | + styleUrls: ['./navigation-card-widget.component.scss'] | |
35 | +}) | |
36 | +export class NavigationCardWidgetComponent extends PageComponent implements OnInit { | |
37 | + | |
38 | + settings: NavigationCardWidgetSettings; | |
39 | + | |
40 | + translatedName: string; | |
41 | + | |
42 | + @Input() | |
43 | + ctx: WidgetContext; | |
44 | + | |
45 | + constructor(protected store: Store<AppState>, | |
46 | + private utils: UtilsService, | |
47 | + private ngZone: NgZone, | |
48 | + private router: Router) { | |
49 | + super(store); | |
50 | + } | |
51 | + | |
52 | + ngOnInit(): void { | |
53 | + this.ctx.$scope.navigationCardWidget = this; | |
54 | + this.settings = this.ctx.settings; | |
55 | + this.translatedName = this.utils.customTranslation(this.settings.name, this.settings.name); | |
56 | + } | |
57 | + | |
58 | + | |
59 | + navigate($event: Event, path: string) { | |
60 | + $event.preventDefault(); | |
61 | + this.ngZone.run(() => { | |
62 | + this.router.navigateByUrl(path); | |
63 | + }); | |
64 | + } | |
65 | + | |
66 | +} | ... | ... |
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016-2021 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<mat-grid-list *ngIf="cols" class="tb-navigation-cards" [cols]="cols" rowHeight="280px"> | |
19 | + <mat-grid-tile [colspan]="sectionColspan(section)" *ngFor="let section of showHomeSections$| async"> | |
20 | + <mat-card style="width: 100%;"> | |
21 | + <mat-card-title> | |
22 | + <span translate class="mat-headline">{{section.name}}</span> | |
23 | + </mat-card-title> | |
24 | + <mat-card-content> | |
25 | + <mat-grid-list rowHeight="170px" [cols]="sectionPlaces(section).length"> | |
26 | + <mat-grid-tile *ngFor="let place of sectionPlaces(section)"> | |
27 | + <a mat-raised-button color="primary" class="tb-card-button" href="{{place.path}}" (click)="navigate($event, place.path)"> | |
28 | + <mat-icon *ngIf="!place.isMdiIcon" class="material-icons tb-mat-96">{{place.icon}}</mat-icon> | |
29 | + <mat-icon *ngIf="place.isMdiIcon" class="tb-mat-96" [svgIcon]="place.icon"></mat-icon> | |
30 | + <span translate>{{place.name}}</span> | |
31 | + </a> | |
32 | + </mat-grid-tile> | |
33 | + </mat-grid-list> | |
34 | + </mat-card-content> | |
35 | + </mat-card> | |
36 | + </mat-grid-tile> | |
37 | +</mat-grid-list> | ... | ... |
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 | +@import '../../../../../../scss/constants'; | |
17 | + | |
18 | +:host { | |
19 | + width: 100%; | |
20 | + height: 100%; | |
21 | +} | |
22 | + | |
23 | +:host ::ng-deep { | |
24 | + .tb-navigation-cards { | |
25 | + .mat-headline { | |
26 | + font-size: 20px; | |
27 | + @media #{$mat-gt-xmd} { | |
28 | + font-size: 24px; | |
29 | + } | |
30 | + } | |
31 | + mat-card { | |
32 | + padding: 0; | |
33 | + margin: 8px; | |
34 | + mat-card-title { | |
35 | + margin: 0; | |
36 | + padding: 24px 16px 16px; | |
37 | + } | |
38 | + mat-card-title+mat-card-content { | |
39 | + padding-top: 0; | |
40 | + } | |
41 | + mat-card-content { | |
42 | + padding: 16px; | |
43 | + } | |
44 | + } | |
45 | + .tb-card-button { | |
46 | + width: 100%; | |
47 | + height: 100%; | |
48 | + max-width: 240px; | |
49 | + &:hover { | |
50 | + border-bottom: none; | |
51 | + } | |
52 | + &:focus { | |
53 | + border-bottom: none; | |
54 | + } | |
55 | + .mat-button-wrapper { | |
56 | + width: 100%; | |
57 | + height: 100%; | |
58 | + display: flex; | |
59 | + flex-direction: column; | |
60 | + align-items: center; | |
61 | + mat-icon { | |
62 | + margin: auto; | |
63 | + } | |
64 | + span { | |
65 | + height: 18px; | |
66 | + min-height: 36px; | |
67 | + max-height: 36px; | |
68 | + padding: 0 0 20px 0; | |
69 | + margin: auto; | |
70 | + font-size: 18px; | |
71 | + font-weight: 400; | |
72 | + line-height: 18px; | |
73 | + white-space: normal; | |
74 | + } | |
75 | + } | |
76 | + &.mat-raised-button.mat-primary { | |
77 | + .mat-ripple-element { | |
78 | + opacity: 0.3; | |
79 | + background-color: rgba(255, 255, 255, 0.3); | |
80 | + } | |
81 | + } | |
82 | + } | |
83 | + } | |
84 | +} | |
85 | + | ... | ... |
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 { PageComponent } from '@shared/components/page.component'; | |
18 | +import { Component, Input, NgZone, OnInit } from '@angular/core'; | |
19 | +import { WidgetContext } from '@home/models/widget-component.models'; | |
20 | +import { Store } from '@ngrx/store'; | |
21 | +import { AppState } from '@core/core.state'; | |
22 | +import { MenuService } from '@core/services/menu.service'; | |
23 | +import { HomeSection, HomeSectionPlace } from '@core/services/menu.models'; | |
24 | +import { Router } from '@angular/router'; | |
25 | +import { map } from 'rxjs/operators'; | |
26 | + | |
27 | +interface NavigationCardsWidgetSettings { | |
28 | + filterType: 'all' | 'include' | 'exclude'; | |
29 | + filter: string[]; | |
30 | +} | |
31 | + | |
32 | +@Component({ | |
33 | + selector: 'tb-navigation-cards-widget', | |
34 | + templateUrl: './navigation-cards-widget.component.html', | |
35 | + styleUrls: ['./navigation-cards-widget.component.scss'] | |
36 | +}) | |
37 | +export class NavigationCardsWidgetComponent extends PageComponent implements OnInit { | |
38 | + | |
39 | + homeSections$ = this.menuService.homeSections(); | |
40 | + showHomeSections$ = this.homeSections$.pipe( | |
41 | + map((sections) => { | |
42 | + return sections.filter((section) => this.sectionPlaces(section).length > 0); | |
43 | + }) | |
44 | + ); | |
45 | + | |
46 | + cols = null; | |
47 | + | |
48 | + settings: NavigationCardsWidgetSettings; | |
49 | + | |
50 | + @Input() | |
51 | + ctx: WidgetContext; | |
52 | + | |
53 | + constructor(protected store: Store<AppState>, | |
54 | + private menuService: MenuService, | |
55 | + private ngZone: NgZone, | |
56 | + private router: Router) { | |
57 | + super(store); | |
58 | + } | |
59 | + | |
60 | + ngOnInit(): void { | |
61 | + this.ctx.$scope.navigationCardsWidget = this; | |
62 | + this.settings = this.ctx.settings; | |
63 | + } | |
64 | + | |
65 | + resize() { | |
66 | + this.updateColumnCount(); | |
67 | + } | |
68 | + | |
69 | + private updateColumnCount() { | |
70 | + this.cols = 2; | |
71 | + const width = this.ctx.width; | |
72 | + if (width >= 1280) { | |
73 | + this.cols = 3; | |
74 | + if (width >= 1920) { | |
75 | + this.cols = 4; | |
76 | + } | |
77 | + } | |
78 | + this.ctx.detectChanges(); | |
79 | + } | |
80 | + | |
81 | + navigate($event: Event, path: string) { | |
82 | + $event.preventDefault(); | |
83 | + this.ngZone.run(() => { | |
84 | + this.router.navigateByUrl(path); | |
85 | + }); | |
86 | + } | |
87 | + | |
88 | + sectionPlaces(section: HomeSection): HomeSectionPlace[] { | |
89 | + return section && section.places ? section.places.filter((place) => this.filterPlace(place)) : []; | |
90 | + } | |
91 | + | |
92 | + private filterPlace(place: HomeSectionPlace): boolean { | |
93 | + if (this.settings.filterType === 'include') { | |
94 | + return this.settings.filter.includes(place.path); | |
95 | + } else if (this.settings.filterType === 'exclude') { | |
96 | + return !this.settings.filter.includes(place.path); | |
97 | + } | |
98 | + return true; | |
99 | + } | |
100 | + | |
101 | + sectionColspan(section: HomeSection): number { | |
102 | + if (this.ctx.width >= 960) { | |
103 | + let colspan = this.cols; | |
104 | + const places = this.sectionPlaces(section); | |
105 | + if (places.length <= colspan) { | |
106 | + colspan = places.length; | |
107 | + } | |
108 | + return colspan; | |
109 | + } else { | |
110 | + return 2; | |
111 | + } | |
112 | + } | |
113 | + | |
114 | +} | ... | ... |
... | ... | @@ -35,6 +35,8 @@ import { TripAnimationComponent } from './trip-animation/trip-animation.componen |
35 | 35 | import { PhotoCameraInputWidgetComponent } from './lib/photo-camera-input.component'; |
36 | 36 | import { GatewayFormComponent } from './lib/gateway/gateway-form.component'; |
37 | 37 | import { ImportExportService } from '@home/components/import-export/import-export.service'; |
38 | +import { NavigationCardsWidgetComponent } from '@home/components/widget/lib/navigation-cards-widget.component'; | |
39 | +import { NavigationCardWidgetComponent } from '@home/components/widget/lib/navigation-card-widget.component'; | |
38 | 40 | |
39 | 41 | @NgModule({ |
40 | 42 | declarations: |
... | ... | @@ -50,7 +52,9 @@ import { ImportExportService } from '@home/components/import-export/import-expor |
50 | 52 | MultipleInputWidgetComponent, |
51 | 53 | TripAnimationComponent, |
52 | 54 | PhotoCameraInputWidgetComponent, |
53 | - GatewayFormComponent | |
55 | + GatewayFormComponent, | |
56 | + NavigationCardsWidgetComponent, | |
57 | + NavigationCardWidgetComponent | |
54 | 58 | ], |
55 | 59 | imports: [ |
56 | 60 | CommonModule, |
... | ... | @@ -68,7 +72,9 @@ import { ImportExportService } from '@home/components/import-export/import-expor |
68 | 72 | MultipleInputWidgetComponent, |
69 | 73 | TripAnimationComponent, |
70 | 74 | PhotoCameraInputWidgetComponent, |
71 | - GatewayFormComponent | |
75 | + GatewayFormComponent, | |
76 | + NavigationCardsWidgetComponent, | |
77 | + NavigationCardWidgetComponent | |
72 | 78 | ], |
73 | 79 | providers: [ |
74 | 80 | CustomDialogService, | ... | ... |
... | ... | @@ -32,6 +32,7 @@ import { getCurrentAuthUser } from '@core/auth/auth.selectors'; |
32 | 32 | import { OAuth2Service } from '@core/http/oauth2.service'; |
33 | 33 | import { UserProfileResolver } from '@home/pages/profile/profile-routing.module'; |
34 | 34 | import { SmsProviderComponent } from '@home/pages/admin/sms-provider.component'; |
35 | +import { HomeSettingsComponent } from '@home/pages/admin/home-settings.component'; | |
35 | 36 | |
36 | 37 | @Injectable() |
37 | 38 | export class OAuth2LoginProcessingUrlResolver implements Resolve<string> { |
... | ... | @@ -48,7 +49,7 @@ const routes: Routes = [ |
48 | 49 | { |
49 | 50 | path: 'settings', |
50 | 51 | data: { |
51 | - auth: [Authority.SYS_ADMIN], | |
52 | + auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], | |
52 | 53 | breadcrumb: { |
53 | 54 | label: 'admin.system-settings', |
54 | 55 | icon: 'settings' |
... | ... | @@ -57,8 +58,13 @@ const routes: Routes = [ |
57 | 58 | children: [ |
58 | 59 | { |
59 | 60 | path: '', |
60 | - redirectTo: 'general', | |
61 | - pathMatch: 'full' | |
61 | + data: { | |
62 | + auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], | |
63 | + redirectTo: { | |
64 | + SYS_ADMIN: '/settings/general', | |
65 | + TENANT_ADMIN: '/settings/home' | |
66 | + } | |
67 | + } | |
62 | 68 | }, |
63 | 69 | { |
64 | 70 | path: 'general', |
... | ... | @@ -127,6 +133,19 @@ const routes: Routes = [ |
127 | 133 | resolve: { |
128 | 134 | loginProcessingUrl: OAuth2LoginProcessingUrlResolver |
129 | 135 | } |
136 | + }, | |
137 | + { | |
138 | + path: 'home', | |
139 | + component: HomeSettingsComponent, | |
140 | + canDeactivate: [ConfirmOnExitGuard], | |
141 | + data: { | |
142 | + auth: [Authority.TENANT_ADMIN], | |
143 | + title: 'admin.home-settings', | |
144 | + breadcrumb: { | |
145 | + label: 'admin.home-settings', | |
146 | + icon: 'settings_applications' | |
147 | + } | |
148 | + } | |
130 | 149 | } |
131 | 150 | ] |
132 | 151 | } | ... | ... |
... | ... | @@ -26,6 +26,7 @@ import { HomeComponentsModule } from '@modules/home/components/home-components.m |
26 | 26 | import { OAuth2SettingsComponent } from '@modules/home/pages/admin/oauth2-settings.component'; |
27 | 27 | import { SmsProviderComponent } from '@home/pages/admin/sms-provider.component'; |
28 | 28 | import { SendTestSmsDialogComponent } from '@home/pages/admin/send-test-sms-dialog.component'; |
29 | +import { HomeSettingsComponent } from '@home/pages/admin/home-settings.component'; | |
29 | 30 | |
30 | 31 | @NgModule({ |
31 | 32 | declarations: |
... | ... | @@ -35,7 +36,8 @@ import { SendTestSmsDialogComponent } from '@home/pages/admin/send-test-sms-dial |
35 | 36 | SmsProviderComponent, |
36 | 37 | SendTestSmsDialogComponent, |
37 | 38 | SecuritySettingsComponent, |
38 | - OAuth2SettingsComponent | |
39 | + OAuth2SettingsComponent, | |
40 | + HomeSettingsComponent | |
39 | 41 | ], |
40 | 42 | imports: [ |
41 | 43 | CommonModule, | ... | ... |
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016-2021 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<div> | |
19 | + <mat-card class="settings-card"> | |
20 | + <mat-card-title> | |
21 | + <div fxLayout="row"> | |
22 | + <span class="mat-headline" translate>admin.home-settings</span> | |
23 | + </div> | |
24 | + </mat-card-title> | |
25 | + <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async"> | |
26 | + </mat-progress-bar> | |
27 | + <div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div> | |
28 | + <mat-card-content style="padding-top: 16px;"> | |
29 | + <form [formGroup]="homeSettings" (ngSubmit)="save()"> | |
30 | + <fieldset [disabled]="isLoading$ | async"> | |
31 | + <section class="tb-default-dashboard" fxFlex fxLayout="column"> | |
32 | + <section fxFlex fxLayout="column" fxLayout.gt-sm="row"> | |
33 | + <tb-dashboard-autocomplete | |
34 | + fxFlex | |
35 | + placeholder="{{ 'dashboard.home-dashboard' | translate }}" | |
36 | + formControlName="dashboardId" | |
37 | + [dashboardsScope]="'tenant'" | |
38 | + [selectFirstDashboard]="false" | |
39 | + ></tb-dashboard-autocomplete> | |
40 | + <mat-checkbox fxFlex formControlName="hideDashboardToolbar"> | |
41 | + {{ 'dashboard.home-dashboard-hide-toolbar' | translate }} | |
42 | + </mat-checkbox> | |
43 | + </section> | |
44 | + </section> | |
45 | + <div fxLayout="row" fxLayoutAlign="end center" style="width: 100%;" class="layout-wrap"> | |
46 | + <button mat-button mat-raised-button color="primary" [disabled]="(isLoading$ | async) || homeSettings.invalid || !homeSettings.dirty" | |
47 | + type="submit">{{'action.save' | translate}} | |
48 | + </button> | |
49 | + </div> | |
50 | + </fieldset> | |
51 | + </form> | |
52 | + </mat-card-content> | |
53 | + </mat-card> | |
54 | +</div> | ... | ... |
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 "../../../../../scss/constants"; | |
18 | + | |
19 | +:host { | |
20 | + .tb-default-dashboard { | |
21 | + tb-dashboard-autocomplete { | |
22 | + @media #{$mat-gt-sm} { | |
23 | + padding-right: 12px; | |
24 | + } | |
25 | + | |
26 | + @media #{$mat-lt-md} { | |
27 | + padding-bottom: 12px; | |
28 | + } | |
29 | + } | |
30 | + mat-checkbox { | |
31 | + @media #{$mat-gt-sm} { | |
32 | + margin-top: 16px; | |
33 | + } | |
34 | + } | |
35 | + } | |
36 | +} | ... | ... |
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 { Component, OnInit } from '@angular/core'; | |
18 | +import { Store } from '@ngrx/store'; | |
19 | +import { AppState } from '@core/core.state'; | |
20 | +import { PageComponent } from '@shared/components/page.component'; | |
21 | +import { Router } from '@angular/router'; | |
22 | +import { FormBuilder, FormGroup } from '@angular/forms'; | |
23 | +import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard'; | |
24 | +import { DashboardService } from '@core/http/dashboard.service'; | |
25 | +import { HomeDashboardInfo } from '@shared/models/dashboard.models'; | |
26 | +import { isDefinedAndNotNull } from '@core/utils'; | |
27 | +import { DashboardId } from '@shared/models/id/dashboard-id'; | |
28 | + | |
29 | +@Component({ | |
30 | + selector: 'tb-home-settings', | |
31 | + templateUrl: './home-settings.component.html', | |
32 | + styleUrls: ['./home-settings.component.scss', './settings-card.scss'] | |
33 | +}) | |
34 | +export class HomeSettingsComponent extends PageComponent implements OnInit, HasConfirmForm { | |
35 | + | |
36 | + homeSettings: FormGroup; | |
37 | + | |
38 | + constructor(protected store: Store<AppState>, | |
39 | + private router: Router, | |
40 | + private dashboardService: DashboardService, | |
41 | + public fb: FormBuilder) { | |
42 | + super(store); | |
43 | + } | |
44 | + | |
45 | + ngOnInit() { | |
46 | + this.homeSettings = this.fb.group({ | |
47 | + dashboardId: [null], | |
48 | + hideDashboardToolbar: [true] | |
49 | + }); | |
50 | + this.dashboardService.getTenantHomeDashboardInfo().subscribe( | |
51 | + (homeDashboardInfo) => { | |
52 | + this.setHomeDashboardInfo(homeDashboardInfo); | |
53 | + } | |
54 | + ); | |
55 | + } | |
56 | + | |
57 | + save(): void { | |
58 | + const strDashboardId = this.homeSettings.get('dashboardId').value; | |
59 | + const dashboardId: DashboardId = strDashboardId ? new DashboardId(strDashboardId) : null; | |
60 | + const hideDashboardToolbar = this.homeSettings.get('hideDashboardToolbar').value; | |
61 | + const homeDashboardInfo: HomeDashboardInfo = { | |
62 | + dashboardId, | |
63 | + hideDashboardToolbar | |
64 | + }; | |
65 | + this.dashboardService.setTenantHomeDashboardInfo(homeDashboardInfo).subscribe( | |
66 | + () => { | |
67 | + this.setHomeDashboardInfo(homeDashboardInfo); | |
68 | + } | |
69 | + ); | |
70 | + } | |
71 | + | |
72 | + confirmForm(): FormGroup { | |
73 | + return this.homeSettings; | |
74 | + } | |
75 | + | |
76 | + private setHomeDashboardInfo(homeDashboardInfo: HomeDashboardInfo) { | |
77 | + this.homeSettings.reset({ | |
78 | + dashboardId: homeDashboardInfo?.dashboardId?.id, | |
79 | + hideDashboardToolbar: isDefinedAndNotNull(homeDashboardInfo?.hideDashboardToolbar) ? | |
80 | + homeDashboardInfo?.hideDashboardToolbar : true | |
81 | + }); | |
82 | + } | |
83 | + | |
84 | +} | ... | ... |
... | ... | @@ -72,6 +72,21 @@ |
72 | 72 | <mat-label translate>customer.description</mat-label> |
73 | 73 | <textarea matInput formControlName="description" rows="2"></textarea> |
74 | 74 | </mat-form-field> |
75 | + <section class="tb-default-dashboard" fxFlex fxLayout="column" *ngIf="entity?.id"> | |
76 | + <section fxFlex fxLayout="column" fxLayout.gt-sm="row"> | |
77 | + <tb-dashboard-autocomplete | |
78 | + fxFlex | |
79 | + placeholder="{{ 'dashboard.home-dashboard' | translate }}" | |
80 | + formControlName="homeDashboardId" | |
81 | + [dashboardsScope]="'customer'" | |
82 | + [customerId]="entity?.id.id" | |
83 | + [selectFirstDashboard]="false" | |
84 | + ></tb-dashboard-autocomplete> | |
85 | + <mat-checkbox fxFlex formControlName="homeDashboardHideToolbar"> | |
86 | + {{ 'dashboard.home-dashboard-hide-toolbar' | translate }} | |
87 | + </mat-checkbox> | |
88 | + </section> | |
89 | + </section> | |
75 | 90 | </div> |
76 | 91 | <tb-contact [parentForm]="entityForm" [isEdit]="isEdit"></tb-contact> |
77 | 92 | </fieldset> | ... | ... |
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 | +@import "../../../../../scss/constants"; | |
17 | + | |
18 | +:host { | |
19 | + .tb-default-dashboard { | |
20 | + tb-dashboard-autocomplete { | |
21 | + @media #{$mat-gt-sm} { | |
22 | + padding-right: 12px; | |
23 | + } | |
24 | + | |
25 | + @media #{$mat-lt-md} { | |
26 | + padding-bottom: 12px; | |
27 | + } | |
28 | + } | |
29 | + mat-checkbox { | |
30 | + @media #{$mat-gt-sm} { | |
31 | + margin-top: 16px; | |
32 | + } | |
33 | + } | |
34 | + } | |
35 | +} | ... | ... |
... | ... | @@ -23,10 +23,12 @@ import { ActionNotificationShow } from '@app/core/notification/notification.acti |
23 | 23 | import { TranslateService } from '@ngx-translate/core'; |
24 | 24 | import { ContactBasedComponent } from '../../components/entity/contact-based.component'; |
25 | 25 | import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; |
26 | +import { isDefinedAndNotNull } from '@core/utils'; | |
26 | 27 | |
27 | 28 | @Component({ |
28 | 29 | selector: 'tb-customer', |
29 | - templateUrl: './customer.component.html' | |
30 | + templateUrl: './customer.component.html', | |
31 | + styleUrls: ['./customer.component.scss'] | |
30 | 32 | }) |
31 | 33 | export class CustomerComponent extends ContactBasedComponent<Customer> { |
32 | 34 | |
... | ... | @@ -54,7 +56,10 @@ export class CustomerComponent extends ContactBasedComponent<Customer> { |
54 | 56 | title: [entity ? entity.title : '', [Validators.required]], |
55 | 57 | additionalInfo: this.fb.group( |
56 | 58 | { |
57 | - description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''] | |
59 | + description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''], | |
60 | + homeDashboardId: [entity && entity.additionalInfo ? entity.additionalInfo.homeDashboardId : null], | |
61 | + homeDashboardHideToolbar: [entity && entity.additionalInfo && | |
62 | + isDefinedAndNotNull(entity.additionalInfo.homeDashboardHideToolbar) ? entity.additionalInfo.homeDashboardHideToolbar : true] | |
58 | 63 | } |
59 | 64 | ) |
60 | 65 | } |
... | ... | @@ -65,6 +70,11 @@ export class CustomerComponent extends ContactBasedComponent<Customer> { |
65 | 70 | this.isPublic = entity.additionalInfo && entity.additionalInfo.isPublic; |
66 | 71 | this.entityForm.patchValue({title: entity.title}); |
67 | 72 | this.entityForm.patchValue({additionalInfo: {description: entity.additionalInfo ? entity.additionalInfo.description : ''}}); |
73 | + this.entityForm.patchValue({additionalInfo: | |
74 | + {homeDashboardId: entity.additionalInfo ? entity.additionalInfo.homeDashboardId : null}}); | |
75 | + this.entityForm.patchValue({additionalInfo: | |
76 | + {homeDashboardHideToolbar: entity.additionalInfo && | |
77 | + isDefinedAndNotNull(entity.additionalInfo.homeDashboardHideToolbar) ? entity.additionalInfo.homeDashboardHideToolbar : true}}); | |
68 | 78 | } |
69 | 79 | |
70 | 80 | onCustomerIdCopied(event) { | ... | ... |
... | ... | @@ -14,11 +14,25 @@ |
14 | 14 | /// limitations under the License. |
15 | 15 | /// |
16 | 16 | |
17 | -import { NgModule } from '@angular/core'; | |
18 | -import { RouterModule, Routes } from '@angular/router'; | |
17 | +import { Injectable, NgModule } from '@angular/core'; | |
18 | +import { Resolve, RouterModule, Routes } from '@angular/router'; | |
19 | 19 | |
20 | 20 | import { HomeLinksComponent } from './home-links.component'; |
21 | 21 | import { Authority } from '@shared/models/authority.enum'; |
22 | +import { Observable } from 'rxjs'; | |
23 | +import { HomeDashboard } from '@shared/models/dashboard.models'; | |
24 | +import { DashboardService } from '@core/http/dashboard.service'; | |
25 | + | |
26 | +@Injectable() | |
27 | +export class HomeDashboardResolver implements Resolve<HomeDashboard> { | |
28 | + | |
29 | + constructor(private dashboardService: DashboardService) { | |
30 | + } | |
31 | + | |
32 | + resolve(): Observable<HomeDashboard> { | |
33 | + return this.dashboardService.getHomeDashboard(); | |
34 | + } | |
35 | +} | |
22 | 36 | |
23 | 37 | const routes: Routes = [ |
24 | 38 | { |
... | ... | @@ -31,12 +45,18 @@ const routes: Routes = [ |
31 | 45 | label: 'home.home', |
32 | 46 | icon: 'home' |
33 | 47 | } |
48 | + }, | |
49 | + resolve: { | |
50 | + homeDashboard: HomeDashboardResolver | |
34 | 51 | } |
35 | 52 | } |
36 | 53 | ]; |
37 | 54 | |
38 | 55 | @NgModule({ |
39 | 56 | imports: [RouterModule.forChild(routes)], |
40 | - exports: [RouterModule] | |
57 | + exports: [RouterModule], | |
58 | + providers: [ | |
59 | + HomeDashboardResolver | |
60 | + ] | |
41 | 61 | }) |
42 | 62 | export class HomeLinksRoutingModule { } | ... | ... |
... | ... | @@ -15,23 +15,26 @@ |
15 | 15 | limitations under the License. |
16 | 16 | |
17 | 17 | --> |
18 | -<mat-grid-list class="tb-home-links" [cols]="cols" rowHeight="280px"> | |
19 | - <mat-grid-tile [colspan]="sectionColspan(section)" *ngFor="let section of homeSections$| async"> | |
20 | - <mat-card style="width: 100%;"> | |
21 | - <mat-card-title> | |
22 | - <span translate class="mat-headline">{{section.name}}</span> | |
23 | - </mat-card-title> | |
24 | - <mat-card-content> | |
25 | - <mat-grid-list rowHeight="170px" [cols]="section.places.length"> | |
26 | - <mat-grid-tile *ngFor="let place of section.places"> | |
27 | - <a mat-raised-button color="primary" class="tb-card-button" routerLink="{{place.path}}"> | |
28 | - <mat-icon *ngIf="!place.isMdiIcon" class="material-icons tb-mat-96">{{place.icon}}</mat-icon> | |
29 | - <mat-icon *ngIf="place.isMdiIcon" class="tb-mat-96" [svgIcon]="place.icon"></mat-icon> | |
30 | - <span translate>{{place.name}}</span> | |
31 | - </a> | |
32 | - </mat-grid-tile> | |
33 | - </mat-grid-list> | |
34 | - </mat-card-content> | |
35 | - </mat-card> | |
36 | - </mat-grid-tile> | |
37 | -</mat-grid-list> | |
18 | +<tb-dashboard-page *ngIf="homeDashboard; else homeLinks" [embedded]="true" [dashboard]="homeDashboard" [hideToolbar]="homeDashboard.hideDashboardToolbar"></tb-dashboard-page> | |
19 | +<ng-template #homeLinks> | |
20 | + <mat-grid-list class="tb-home-links" [cols]="cols" rowHeight="280px"> | |
21 | + <mat-grid-tile [colspan]="sectionColspan(section)" *ngFor="let section of homeSections$| async"> | |
22 | + <mat-card style="width: 100%;"> | |
23 | + <mat-card-title> | |
24 | + <span translate class="mat-headline">{{section.name}}</span> | |
25 | + </mat-card-title> | |
26 | + <mat-card-content> | |
27 | + <mat-grid-list rowHeight="170px" [cols]="section.places.length"> | |
28 | + <mat-grid-tile *ngFor="let place of section.places"> | |
29 | + <a mat-raised-button color="primary" class="tb-card-button" routerLink="{{place.path}}"> | |
30 | + <mat-icon *ngIf="!place.isMdiIcon" class="material-icons tb-mat-96">{{place.icon}}</mat-icon> | |
31 | + <mat-icon *ngIf="place.isMdiIcon" class="tb-mat-96" [svgIcon]="place.icon"></mat-icon> | |
32 | + <span translate>{{place.name}}</span> | |
33 | + </a> | |
34 | + </mat-grid-tile> | |
35 | + </mat-grid-list> | |
36 | + </mat-card-content> | |
37 | + </mat-card> | |
38 | + </mat-grid-tile> | |
39 | + </mat-grid-list> | |
40 | +</ng-template> | ... | ... |
... | ... | @@ -19,6 +19,8 @@ import { MenuService } from '@core/services/menu.service'; |
19 | 19 | import { BreakpointObserver, BreakpointState } from '@angular/cdk/layout'; |
20 | 20 | import { MediaBreakpoints } from '@shared/models/constants'; |
21 | 21 | import { HomeSection } from '@core/services/menu.models'; |
22 | +import { ActivatedRoute } from '@angular/router'; | |
23 | +import { HomeDashboard } from '@shared/models/dashboard.models'; | |
22 | 24 | |
23 | 25 | @Component({ |
24 | 26 | selector: 'tb-home-links', |
... | ... | @@ -31,15 +33,20 @@ export class HomeLinksComponent implements OnInit { |
31 | 33 | |
32 | 34 | cols = 2; |
33 | 35 | |
36 | + homeDashboard: HomeDashboard = this.route.snapshot.data.homeDashboard; | |
37 | + | |
34 | 38 | constructor(private menuService: MenuService, |
35 | - public breakpointObserver: BreakpointObserver) { | |
39 | + public breakpointObserver: BreakpointObserver, | |
40 | + private route: ActivatedRoute) { | |
36 | 41 | } |
37 | 42 | |
38 | 43 | ngOnInit() { |
39 | - this.updateColumnCount(); | |
40 | - this.breakpointObserver | |
41 | - .observe([MediaBreakpoints.lg, MediaBreakpoints['gt-lg']]) | |
42 | - .subscribe((state: BreakpointState) => this.updateColumnCount()); | |
44 | + if (!this.homeDashboard) { | |
45 | + this.updateColumnCount(); | |
46 | + this.breakpointObserver | |
47 | + .observe([MediaBreakpoints.lg, MediaBreakpoints['gt-lg']]) | |
48 | + .subscribe((state: BreakpointState) => this.updateColumnCount()); | |
49 | + } | |
43 | 50 | } |
44 | 51 | |
45 | 52 | private updateColumnCount() { | ... | ... |
... | ... | @@ -20,6 +20,7 @@ import { CommonModule } from '@angular/common'; |
20 | 20 | import { HomeLinksRoutingModule } from './home-links-routing.module'; |
21 | 21 | import { HomeLinksComponent } from './home-links.component'; |
22 | 22 | import { SharedModule } from '@app/shared/shared.module'; |
23 | +import { HomeComponentsModule } from '@home/components/home-components.module'; | |
23 | 24 | |
24 | 25 | @NgModule({ |
25 | 26 | declarations: |
... | ... | @@ -29,6 +30,7 @@ import { SharedModule } from '@app/shared/shared.module'; |
29 | 30 | imports: [ |
30 | 31 | CommonModule, |
31 | 32 | SharedModule, |
33 | + HomeComponentsModule, | |
32 | 34 | HomeLinksRoutingModule |
33 | 35 | ] |
34 | 36 | }) | ... | ... |
... | ... | @@ -63,6 +63,20 @@ |
63 | 63 | </mat-option> |
64 | 64 | </mat-select> |
65 | 65 | </mat-form-field> |
66 | + <section class="tb-home-dashboard" fxFlex fxLayout="column" fxLayout.gt-sm="row"> | |
67 | + <tb-dashboard-autocomplete | |
68 | + fxFlex | |
69 | + placeholder="{{ 'dashboard.home-dashboard' | translate }}" | |
70 | + formControlName="homeDashboardId" | |
71 | + [dashboardsScope]="user?.authority === authorities.TENANT_ADMIN ? 'tenant' : 'customer'" | |
72 | + [tenantId]="user?.tenantId?.id" | |
73 | + [customerId]="user?.customerId?.id" | |
74 | + [selectFirstDashboard]="false" | |
75 | + ></tb-dashboard-autocomplete> | |
76 | + <mat-checkbox fxFlex formControlName="homeDashboardHideToolbar"> | |
77 | + {{ 'dashboard.home-dashboard-hide-toolbar' | translate }} | |
78 | + </mat-checkbox> | |
79 | + </section> | |
66 | 80 | <div fxLayout="row" style="padding-bottom: 16px;"> |
67 | 81 | <button mat-button mat-raised-button color="primary" |
68 | 82 | type="button" | ... | ... |
... | ... | @@ -38,5 +38,21 @@ |
38 | 38 | font-size: 16px; |
39 | 39 | font-weight: 400; |
40 | 40 | } |
41 | + .tb-home-dashboard { | |
42 | + tb-dashboard-autocomplete { | |
43 | + @media #{$mat-gt-sm} { | |
44 | + padding-right: 12px; | |
45 | + } | |
46 | + | |
47 | + @media #{$mat-lt-md} { | |
48 | + padding-bottom: 12px; | |
49 | + } | |
50 | + } | |
51 | + mat-checkbox { | |
52 | + @media #{$mat-gt-sm} { | |
53 | + margin-top: 16px; | |
54 | + } | |
55 | + } | |
56 | + } | |
41 | 57 | } |
42 | 58 | } | ... | ... |
... | ... | @@ -32,6 +32,7 @@ import { MatDialog } from '@angular/material/dialog'; |
32 | 32 | import { DialogService } from '@core/services/dialog.service'; |
33 | 33 | import { AuthService } from '@core/auth/auth.service'; |
34 | 34 | import { ActivatedRoute } from '@angular/router'; |
35 | +import { isDefinedAndNotNull } from '@core/utils'; | |
35 | 36 | |
36 | 37 | @Component({ |
37 | 38 | selector: 'tb-profile', |
... | ... | @@ -66,7 +67,9 @@ export class ProfileComponent extends PageComponent implements OnInit, HasConfir |
66 | 67 | email: ['', [Validators.required, Validators.email]], |
67 | 68 | firstName: [''], |
68 | 69 | lastName: [''], |
69 | - language: [''] | |
70 | + language: [''], | |
71 | + homeDashboardId: [null], | |
72 | + homeDashboardHideToolbar: [true] | |
70 | 73 | }); |
71 | 74 | } |
72 | 75 | |
... | ... | @@ -76,6 +79,8 @@ export class ProfileComponent extends PageComponent implements OnInit, HasConfir |
76 | 79 | this.user.additionalInfo = {}; |
77 | 80 | } |
78 | 81 | this.user.additionalInfo.lang = this.profile.get('language').value; |
82 | + this.user.additionalInfo.homeDashboardId = this.profile.get('homeDashboardId').value; | |
83 | + this.user.additionalInfo.homeDashboardHideToolbar = this.profile.get('homeDashboardHideToolbar').value; | |
79 | 84 | this.userService.saveUser(this.user).subscribe( |
80 | 85 | (user) => { |
81 | 86 | this.userLoaded(user); |
... | ... | @@ -106,12 +111,23 @@ export class ProfileComponent extends PageComponent implements OnInit, HasConfir |
106 | 111 | this.user = user; |
107 | 112 | this.profile.reset(user); |
108 | 113 | let lang; |
109 | - if (user.additionalInfo && user.additionalInfo.lang) { | |
110 | - lang = user.additionalInfo.lang; | |
111 | - } else { | |
114 | + let homeDashboardId; | |
115 | + let homeDashboardHideToolbar = true; | |
116 | + if (user.additionalInfo) { | |
117 | + if (user.additionalInfo.lang) { | |
118 | + lang = user.additionalInfo.lang; | |
119 | + } | |
120 | + homeDashboardId = user.additionalInfo.homeDashboardId; | |
121 | + if (isDefinedAndNotNull(user.additionalInfo.homeDashboardHideToolbar)) { | |
122 | + homeDashboardHideToolbar = user.additionalInfo.homeDashboardHideToolbar; | |
123 | + } | |
124 | + } | |
125 | + if (!lang) { | |
112 | 126 | lang = this.translate.currentLang; |
113 | 127 | } |
114 | 128 | this.profile.get('language').setValue(lang); |
129 | + this.profile.get('homeDashboardId').setValue(homeDashboardId); | |
130 | + this.profile.get('homeDashboardHideToolbar').setValue(homeDashboardHideToolbar); | |
115 | 131 | } |
116 | 132 | |
117 | 133 | confirmForm(): FormGroup { | ... | ... |
... | ... | @@ -60,6 +60,21 @@ |
60 | 60 | <mat-label translate>tenant.description</mat-label> |
61 | 61 | <textarea matInput formControlName="description" rows="2"></textarea> |
62 | 62 | </mat-form-field> |
63 | + <section class="tb-default-dashboard" fxFlex fxLayout="column" *ngIf="entity?.id"> | |
64 | + <section fxFlex fxLayout="column" fxLayout.gt-sm="row"> | |
65 | + <tb-dashboard-autocomplete | |
66 | + fxFlex | |
67 | + placeholder="{{ 'dashboard.home-dashboard' | translate }}" | |
68 | + formControlName="homeDashboardId" | |
69 | + [dashboardsScope]="'tenant'" | |
70 | + [tenantId]="entity?.id.id" | |
71 | + [selectFirstDashboard]="false" | |
72 | + ></tb-dashboard-autocomplete> | |
73 | + <mat-checkbox fxFlex formControlName="homeDashboardHideToolbar"> | |
74 | + {{ 'dashboard.home-dashboard-hide-toolbar' | translate }} | |
75 | + </mat-checkbox> | |
76 | + </section> | |
77 | + </section> | |
63 | 78 | </div> |
64 | 79 | <tb-contact [parentForm]="entityForm" [isEdit]="isEdit"></tb-contact> |
65 | 80 | </fieldset> | ... | ... |
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 | +@import "../../../../../scss/constants"; | |
17 | + | |
18 | +:host { | |
19 | + .tb-default-dashboard { | |
20 | + tb-dashboard-autocomplete { | |
21 | + @media #{$mat-gt-sm} { | |
22 | + padding-right: 12px; | |
23 | + } | |
24 | + | |
25 | + @media #{$mat-lt-md} { | |
26 | + padding-bottom: 12px; | |
27 | + } | |
28 | + } | |
29 | + mat-checkbox { | |
30 | + @media #{$mat-gt-sm} { | |
31 | + margin-top: 16px; | |
32 | + } | |
33 | + } | |
34 | + } | |
35 | +} | ... | ... |
... | ... | @@ -23,11 +23,12 @@ import { ActionNotificationShow } from '@app/core/notification/notification.acti |
23 | 23 | import { TranslateService } from '@ngx-translate/core'; |
24 | 24 | import { ContactBasedComponent } from '../../components/entity/contact-based.component'; |
25 | 25 | import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; |
26 | +import { isDefinedAndNotNull } from '@core/utils'; | |
26 | 27 | |
27 | 28 | @Component({ |
28 | 29 | selector: 'tb-tenant', |
29 | 30 | templateUrl: './tenant.component.html', |
30 | - styleUrls: [] | |
31 | + styleUrls: ['./tenant.component.scss'] | |
31 | 32 | }) |
32 | 33 | export class TenantComponent extends ContactBasedComponent<TenantInfo> { |
33 | 34 | |
... | ... | @@ -54,7 +55,10 @@ export class TenantComponent extends ContactBasedComponent<TenantInfo> { |
54 | 55 | tenantProfileId: [entity ? entity.tenantProfileId : null, [Validators.required]], |
55 | 56 | additionalInfo: this.fb.group( |
56 | 57 | { |
57 | - description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''] | |
58 | + description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''], | |
59 | + homeDashboardId: [entity && entity.additionalInfo ? entity.additionalInfo.homeDashboardId : null], | |
60 | + homeDashboardHideToolbar: [entity && entity.additionalInfo && | |
61 | + isDefinedAndNotNull(entity.additionalInfo.homeDashboardHideToolbar) ? entity.additionalInfo.homeDashboardHideToolbar : true] | |
58 | 62 | } |
59 | 63 | ) |
60 | 64 | } |
... | ... | @@ -65,6 +69,11 @@ export class TenantComponent extends ContactBasedComponent<TenantInfo> { |
65 | 69 | this.entityForm.patchValue({title: entity.title}); |
66 | 70 | this.entityForm.patchValue({tenantProfileId: entity.tenantProfileId}); |
67 | 71 | this.entityForm.patchValue({additionalInfo: {description: entity.additionalInfo ? entity.additionalInfo.description : ''}}); |
72 | + this.entityForm.patchValue({additionalInfo: | |
73 | + {homeDashboardId: entity.additionalInfo ? entity.additionalInfo.homeDashboardId : null}}); | |
74 | + this.entityForm.patchValue({additionalInfo: | |
75 | + {homeDashboardHideToolbar: entity.additionalInfo && | |
76 | + isDefinedAndNotNull(entity.additionalInfo.homeDashboardHideToolbar) ? entity.additionalInfo.homeDashboardHideToolbar : true}}); | |
68 | 77 | } |
69 | 78 | |
70 | 79 | updateFormState() { | ... | ... |
... | ... | @@ -95,6 +95,20 @@ |
95 | 95 | {{ 'user.always-fullscreen' | translate }} |
96 | 96 | </mat-checkbox> |
97 | 97 | </section> |
98 | + <section fxFlex fxLayout="column" fxLayout.gt-sm="row"> | |
99 | + <tb-dashboard-autocomplete | |
100 | + fxFlex | |
101 | + placeholder="{{ 'dashboard.home-dashboard' | translate }}" | |
102 | + formControlName="homeDashboardId" | |
103 | + [dashboardsScope]="entity?.authority === authority.TENANT_ADMIN ? 'tenant' : 'customer'" | |
104 | + [tenantId]="entity?.tenantId?.id" | |
105 | + [customerId]="entity?.customerId?.id" | |
106 | + [selectFirstDashboard]="false" | |
107 | + ></tb-dashboard-autocomplete> | |
108 | + <mat-checkbox fxFlex formControlName="homeDashboardHideToolbar"> | |
109 | + {{ 'dashboard.home-dashboard-hide-toolbar' | translate }} | |
110 | + </mat-checkbox> | |
111 | + </section> | |
98 | 112 | </section> |
99 | 113 | </div> |
100 | 114 | </fieldset> | ... | ... |
... | ... | @@ -23,7 +23,7 @@ import { User } from '@shared/models/user.model'; |
23 | 23 | import { selectAuth } from '@core/auth/auth.selectors'; |
24 | 24 | import { map } from 'rxjs/operators'; |
25 | 25 | import { Authority } from '@shared/models/authority.enum'; |
26 | -import { isUndefined } from '@core/utils'; | |
26 | +import { isDefinedAndNotNull, isUndefined } from '@core/utils'; | |
27 | 27 | import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; |
28 | 28 | |
29 | 29 | @Component({ |
... | ... | @@ -74,6 +74,9 @@ export class UserComponent extends EntityComponent<User> { |
74 | 74 | description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''], |
75 | 75 | defaultDashboardId: [entity && entity.additionalInfo ? entity.additionalInfo.defaultDashboardId : null], |
76 | 76 | defaultDashboardFullscreen: [entity && entity.additionalInfo ? entity.additionalInfo.defaultDashboardFullscreen : false], |
77 | + homeDashboardId: [entity && entity.additionalInfo ? entity.additionalInfo.homeDashboardId : null], | |
78 | + homeDashboardHideToolbar: [entity && entity.additionalInfo && | |
79 | + isDefinedAndNotNull(entity.additionalInfo.homeDashboardHideToolbar) ? entity.additionalInfo.homeDashboardHideToolbar : true] | |
77 | 80 | } |
78 | 81 | ) |
79 | 82 | } |
... | ... | @@ -89,6 +92,11 @@ export class UserComponent extends EntityComponent<User> { |
89 | 92 | {defaultDashboardId: entity.additionalInfo ? entity.additionalInfo.defaultDashboardId : null}}); |
90 | 93 | this.entityForm.patchValue({additionalInfo: |
91 | 94 | {defaultDashboardFullscreen: entity.additionalInfo ? entity.additionalInfo.defaultDashboardFullscreen : false}}); |
95 | + this.entityForm.patchValue({additionalInfo: | |
96 | + {homeDashboardId: entity.additionalInfo ? entity.additionalInfo.homeDashboardId : null}}); | |
97 | + this.entityForm.patchValue({additionalInfo: | |
98 | + {homeDashboardHideToolbar: entity.additionalInfo && | |
99 | + isDefinedAndNotNull(entity.additionalInfo.homeDashboardHideToolbar) ? entity.additionalInfo.homeDashboardHideToolbar : true}}); | |
92 | 100 | } |
93 | 101 | |
94 | 102 | } | ... | ... |
... | ... | @@ -35,6 +35,7 @@ |
35 | 35 | [isEditActionEnabled]="true" |
36 | 36 | [isExportActionEnabled]="true" |
37 | 37 | [isRemoveActionEnabled]="!isReadOnly" |
38 | + [disableWidgetInteraction]="true" | |
38 | 39 | [callbacks]="dashboardCallbacks"></tb-dashboard> |
39 | 40 | <tb-footer-fab-buttons [fxShow]="!isReadOnly" [footerFabButtons]="footerFabButtons"> |
40 | 41 | </tb-footer-fab-buttons> | ... | ... |
... | ... | @@ -28,12 +28,17 @@ class ThingsboardRadios extends React.Component<JsonFormFieldProps, JsonFormFiel |
28 | 28 | ); |
29 | 29 | }); |
30 | 30 | |
31 | + let row = false; | |
32 | + if (this.props.form.direction === 'row') { | |
33 | + row = true; | |
34 | + } | |
35 | + | |
31 | 36 | return ( |
32 | 37 | <FormControl component='fieldset' |
33 | 38 | className={this.props.form.htmlClass} |
34 | 39 | disabled={this.props.form.readonly}> |
35 | 40 | <FormLabel component='legend'>{this.props.form.title}</FormLabel> |
36 | - <RadioGroup name={this.props.form.title} value={this.props.value} onChange={(e) => { | |
41 | + <RadioGroup row={row} name={this.props.form.title} value={this.props.value} onChange={(e) => { | |
37 | 42 | this.props.onChangeValidate(e); |
38 | 43 | }}> |
39 | 44 | {items} | ... | ... |
... | ... | @@ -22,6 +22,7 @@ import { |
22 | 22 | KeyLabelItem |
23 | 23 | } from '@shared/components/json-form/react/json-form.models'; |
24 | 24 | import { Mode } from 'rc-select/lib/interface'; |
25 | +import { deepClone } from '@core/utils'; | |
25 | 26 | |
26 | 27 | interface ThingsboardRcSelectState extends JsonFormFieldState { |
27 | 28 | currentValue: KeyLabelItem | KeyLabelItem[]; |
... | ... | @@ -151,10 +152,14 @@ class ThingsboardRcSelect extends React.Component<JsonFormFieldProps, Thingsboar |
151 | 152 | labelClass += ' tb-focused'; |
152 | 153 | } |
153 | 154 | let mode: Mode; |
154 | - if (this.props.form.tags) { | |
155 | - mode = 'tags'; | |
156 | - } else if (this.props.form.multiple) { | |
157 | - mode = 'multiple'; | |
155 | + let value = this.state.currentValue; | |
156 | + if (this.props.form.tags || this.props.form.multiple) { | |
157 | + value = deepClone(value); | |
158 | + if (this.props.form.tags) { | |
159 | + mode = 'tags'; | |
160 | + } else if (this.props.form.multiple) { | |
161 | + mode = 'multiple'; | |
162 | + } | |
158 | 163 | } |
159 | 164 | |
160 | 165 | const dropdownStyle = {...this.props.form.dropdownStyle, ...{zIndex: 100001}}; |
... | ... | @@ -176,12 +181,13 @@ class ThingsboardRcSelect extends React.Component<JsonFormFieldProps, Thingsboar |
176 | 181 | maxTagTextLength={this.props.form.maxTagTextLength} |
177 | 182 | disabled={this.props.form.readonly} |
178 | 183 | optionLabelProp='children' |
179 | - value={this.state.currentValue} | |
184 | + value={value} | |
180 | 185 | labelInValue={true} |
181 | 186 | onSelect={this.onSelect} |
182 | 187 | onDeselect={this.onDeselect} |
183 | 188 | onFocus={this.onFocus} |
184 | 189 | onBlur={this.onBlur} |
190 | + placeholder={this.props.form.placeholder} | |
185 | 191 | style={this.props.form.style || {width: '100%'}}> |
186 | 192 | {options} |
187 | 193 | </Select> | ... | ... |
... | ... | @@ -106,6 +106,15 @@ export interface Dashboard extends DashboardInfo { |
106 | 106 | configuration?: DashboardConfiguration; |
107 | 107 | } |
108 | 108 | |
109 | +export interface HomeDashboard extends Dashboard { | |
110 | + hideDashboardToolbar: boolean; | |
111 | +} | |
112 | + | |
113 | +export interface HomeDashboardInfo { | |
114 | + dashboardId: DashboardId; | |
115 | + hideDashboardToolbar: boolean; | |
116 | +} | |
117 | + | |
109 | 118 | export function isPublicDashboard(dashboard: DashboardInfo): boolean { |
110 | 119 | if (dashboard && dashboard.assignedCustomers) { |
111 | 120 | return dashboard.assignedCustomers | ... | ... |
... | ... | @@ -74,6 +74,7 @@ |
74 | 74 | "admin": { |
75 | 75 | "general": "General", |
76 | 76 | "general-settings": "General Settings", |
77 | + "home-settings": "Home Settings", | |
77 | 78 | "outgoing-mail": "Mail Server", |
78 | 79 | "outgoing-mail-settings": "Outgoing Mail Server Settings", |
79 | 80 | "system-settings": "System Settings", |
... | ... | @@ -764,7 +765,9 @@ |
764 | 765 | "select-state": "Select target state", |
765 | 766 | "state-controller": "State controller", |
766 | 767 | "search": "Search dashboards", |
767 | - "selected-dashboards": "{ count, plural, 1 {1 dashboard} other {# dashboards} } selected" | |
768 | + "selected-dashboards": "{ count, plural, 1 {1 dashboard} other {# dashboards} } selected", | |
769 | + "home-dashboard": "Home dashboard", | |
770 | + "home-dashboard-hide-toolbar": "Hide home dashboard toolbar" | |
768 | 771 | }, |
769 | 772 | "datakey": { |
770 | 773 | "settings": "Settings", | ... | ... |
... | ... | @@ -1118,6 +1118,13 @@ |
1118 | 1118 | dependencies: |
1119 | 1119 | regenerator-runtime "^0.13.4" |
1120 | 1120 | |
1121 | +"@babel/runtime@^7.12.5": | |
1122 | + version "7.12.5" | |
1123 | + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" | |
1124 | + integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== | |
1125 | + dependencies: | |
1126 | + regenerator-runtime "^0.13.4" | |
1127 | + | |
1121 | 1128 | "@babel/template@7.10.4", "@babel/template@^7.10.4": |
1122 | 1129 | version "7.10.4" |
1123 | 1130 | resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" |
... | ... | @@ -7941,7 +7948,7 @@ rc-util@^4.15.3: |
7941 | 7948 | react-lifecycles-compat "^3.0.4" |
7942 | 7949 | shallowequal "^1.1.0" |
7943 | 7950 | |
7944 | -rc-util@^5.0.0, rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.3.0: | |
7951 | +rc-util@^5.0.0, rc-util@^5.0.1, rc-util@^5.3.0: | |
7945 | 7952 | version "5.4.0" |
7946 | 7953 | resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.4.0.tgz#688eaeecfdae9dae2bfdf10bedbe884591dba004" |
7947 | 7954 | integrity sha512-kXDn1JyLJTAWLBFt+fjkTcUtXhxKkipQCobQmxIEVrX62iXgo24z8YKoWehWfMxPZFPE+RXqrmEu9j5kHz/Lrg== |
... | ... | @@ -7949,6 +7956,15 @@ rc-util@^5.0.0, rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.3.0: |
7949 | 7956 | react-is "^16.12.0" |
7950 | 7957 | shallowequal "^1.1.0" |
7951 | 7958 | |
7959 | +rc-util@^5.0.6: | |
7960 | + version "5.7.0" | |
7961 | + resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.7.0.tgz#776b14cf5bbfc24f419fd40c42ffadddda0718fc" | |
7962 | + integrity sha512-0hh5XkJ+vBDeMJsHElqT1ijMx+gC3gpClwQ10h/5hccrrgrMx8VUem183KLlH1YrWCfMMPmDXWWNnwsn+p6URw== | |
7963 | + dependencies: | |
7964 | + "@babel/runtime" "^7.12.5" | |
7965 | + react-is "^16.12.0" | |
7966 | + shallowequal "^1.1.0" | |
7967 | + | |
7952 | 7968 | rc-virtual-list@^1.1.2: |
7953 | 7969 | version "1.1.6" |
7954 | 7970 | resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-1.1.6.tgz#b255baf9aacde149a8893324e6307214094f4c0a" | ... | ... |