diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/doc-links-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/doc-links-widget.component.ts
index 1c7c5885b5..6517b7dfbd 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/doc-links-widget.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/doc-links-widget.component.ts
@@ -24,10 +24,6 @@ import { DocumentationLink, DocumentationLinks } from '@shared/models/user-setti
import { UserSettingsService } from '@core/http/user-settings.service';
import { getCurrentAuthUser } from '@core/auth/auth.selectors';
import { WidgetContext } from '@home/models/widget-component.models';
-import {
- ImportDialogCsvComponent,
- ImportDialogCsvData
-} from '@home/components/import-export/import-dialog-csv.component';
import { MatDialog } from '@angular/material/dialog';
import { AddDocLinkDialogComponent } from '@home/components/widget/lib/home-page/add-doc-link-dialog.component';
import {
@@ -100,7 +96,7 @@ interface DocLinksWidgetSettings {
@Component({
selector: 'tb-doc-links-widget',
templateUrl: './doc-links-widget.component.html',
- styleUrls: ['./doc-links-widget.component.scss']
+ styleUrls: ['./home-page-widget.scss', './links-widget.component.scss']
})
export class DocLinksWidgetComponent extends PageComponent implements OnInit, OnDestroy {
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/getting-started-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/getting-started-widget.component.html
index ee84a63ad6..170f51796f 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/getting-started-widget.component.html
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/getting-started-widget.component.html
@@ -111,10 +111,21 @@
-
- Ubuntu
- MacOS
- Windows
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/home-page-widget.scss b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/home-page-widget.scss
new file mode 100644
index 0000000000..912ebbb63e
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/home-page-widget.scss
@@ -0,0 +1,70 @@
+/**
+ * Copyright © 2016-2023 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@import "../../../../../../../scss/constants";
+
+:host {
+
+ .tb-card-content {
+ width: 100%;
+ height: 100%;
+ }
+
+ .tb-title {
+ font-style: normal;
+ font-weight: 500;
+ font-size: 14px;
+ line-height: 20px;
+ letter-spacing: 0.25px;
+ color: rgba(0, 0, 0, 0.54);
+ @media #{$mat-md-lg} {
+ font-size: 12px;
+ line-height: 16px;
+ }
+ }
+
+ .tb-title-link {
+ font-style: normal;
+ font-weight: 500;
+ font-size: 14px;
+ line-height: 20px;
+ letter-spacing: 0.25px;
+ color: rgba(0, 0, 0, 0.54);
+ position: relative;
+ border-bottom: none;
+ @media #{$mat-md-lg} {
+ font-size: 12px;
+ line-height: 16px;
+ }
+ &:hover, &:focus {
+ border-bottom: none;
+ }
+ &::after {
+ content: 'arrow_forward';
+ display: inline-block;
+ transform: rotate(315deg);
+ font-family: 'Material Icons';
+ font-weight: normal;
+ font-style: normal;
+ font-size: 18px;
+ color: rgba(0, 0, 0, 0.12);
+ vertical-align: bottom;
+ margin-left: 6px;
+ }
+ &:hover::after {
+ color: inherit;
+ }
+ }
+}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/home-page-widgets.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/home-page-widgets.module.ts
index debd1d6ca0..948f1ebd81 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/home-page-widgets.module.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/home-page-widgets.module.ts
@@ -29,6 +29,8 @@ import {
GettingStartedCompletedDialogComponent
} from '@home/components/widget/lib/home-page/getting-started-completed-dialog.component';
import { ToggleHeaderComponent } from '@home/components/widget/lib/home-page/toggle-header.component';
+import { UsageInfoWidgetComponent } from '@home/components/widget/lib/home-page/usage-info-widget.component';
+import { QuickLinksWidgetComponent } from '@home/components/widget/lib/home-page/quick-links-widget.component';
@NgModule({
declarations:
@@ -42,7 +44,9 @@ import { ToggleHeaderComponent } from '@home/components/widget/lib/home-page/tog
EditDocLinksDialogComponent,
GettingStartedWidgetComponent,
GettingStartedCompletedDialogComponent,
- ToggleHeaderComponent
+ ToggleHeaderComponent,
+ UsageInfoWidgetComponent,
+ QuickLinksWidgetComponent
],
imports: [
CommonModule,
@@ -58,7 +62,9 @@ import { ToggleHeaderComponent } from '@home/components/widget/lib/home-page/tog
EditDocLinksDialogComponent,
GettingStartedWidgetComponent,
GettingStartedCompletedDialogComponent,
- ToggleHeaderComponent
+ ToggleHeaderComponent,
+ UsageInfoWidgetComponent,
+ QuickLinksWidgetComponent
]
})
export class HomePageWidgetsModule { }
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/doc-links-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/links-widget.component.scss
similarity index 73%
rename from ui-ngx/src/app/modules/home/components/widget/lib/home-page/doc-links-widget.component.scss
rename to ui-ngx/src/app/modules/home/components/widget/lib/home-page/links-widget.component.scss
index ddc5a40eec..3e52d6451d 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/doc-links-widget.component.scss
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/links-widget.component.scss
@@ -17,11 +17,6 @@
@import "../../../../../../../scss/constants";
:host {
- .tb-card-content {
- width: 100%;
- height: 100%;
- }
-
.tb-card-header {
display: flex;
flex-direction: row;
@@ -29,39 +24,6 @@
align-items: flex-end;
}
- .tb-title-link {
- font-style: normal;
- font-weight: 500;
- font-size: 14px;
- line-height: 20px;
- letter-spacing: 0.25px;
- color: rgba(0, 0, 0, 0.54);
- position: relative;
- border-bottom: none;
- @media #{$mat-md-lg} {
- font-size: 12px;
- line-height: 16px;
- }
- &:hover, &:focus {
- border-bottom: none;
- }
- &::after {
- content: 'arrow_forward';
- display: inline-block;
- transform: rotate(315deg);
- font-family: 'Material Icons';
- font-weight: normal;
- font-style: normal;
- font-size: 18px;
- color: rgba(0, 0, 0, 0.12);
- vertical-align: bottom;
- margin-left: 6px;
- }
- &:hover::after {
- color: inherit;
- }
- }
-
.tb-title-icon {
color: rgba(0, 0, 0, 0.38);
width: 32px;
@@ -69,15 +31,15 @@
padding: 4px;
}
- .tb-docs-list {
+ .tb-links-list {
overflow: auto;
}
- .mat-grid-tile.tb-docs-tile {
+ .mat-grid-tile.tb-links-tile {
overflow: visible;
}
- .tb-doc-button {
+ .tb-link-button {
height: 55px;
display: flex;
overflow: hidden;
@@ -96,11 +58,11 @@
border: 1px solid rgba(0, 0, 0, 0.12);
box-shadow: 0 4px 10px rgba(23, 33, 90, 0.08);
}
- .tb-doc-container {
+ .tb-link-container {
display: flex;
flex-direction: row;
align-items: center;
- .tb-doc-icon-container {
+ .tb-link-icon-container {
height: 40px;
padding: 8px;
background: #F3F6FA;
@@ -110,7 +72,7 @@
display: none;
}
}
- .tb-doc-text {
+ .tb-link-text {
font-weight: 400;
font-size: 14px;
line-height: 20px;
@@ -127,7 +89,7 @@
}
}
- .tb-add-doc-button {
+ .tb-add-link-button {
height: 55px;
cursor: pointer;
display: flex;
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/quick-links-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/quick-links-widget.component.html
new file mode 100644
index 0000000000..22686c4389
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/quick-links-widget.component.html
@@ -0,0 +1,51 @@
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/quick-links-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/quick-links-widget.component.ts
new file mode 100644
index 0000000000..10f9a8057c
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/quick-links-widget.component.ts
@@ -0,0 +1,162 @@
+///
+/// Copyright © 2016-2023 The Thingsboard Authors
+///
+/// Licensed under the Apache License, Version 2.0 (the "License");
+/// you may not use this file except in compliance with the License.
+/// You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing, software
+/// distributed under the License is distributed on an "AS IS" BASIS,
+/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+/// See the License for the specific language governing permissions and
+/// limitations under the License.
+///
+
+import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
+import { PageComponent } from '@shared/components/page.component';
+import { Store } from '@ngrx/store';
+import { AppState } from '@core/core.state';
+import { Authority } from '@shared/models/authority.enum';
+import { map, Observable, of, Subscription } from 'rxjs';
+import { QuickLinks } from '@shared/models/user-settings.models';
+import { UserSettingsService } from '@core/http/user-settings.service';
+import { getCurrentAuthUser } from '@core/auth/auth.selectors';
+import { WidgetContext } from '@home/models/widget-component.models';
+import { MatDialog } from '@angular/material/dialog';
+import { BreakpointObserver, BreakpointState } from '@angular/cdk/layout';
+import { MediaBreakpoints } from '@shared/models/constants';
+import { MenuService } from '@core/services/menu.service';
+import { MenuSection } from '@core/services/menu.models';
+
+const defaultQuickLinksMap = new Map(
+ [
+ [Authority.SYS_ADMIN, {
+ links: ['tenants', 'tenantProfiles']
+ }],
+ [Authority.TENANT_ADMIN, {
+ links: ['alarms', 'dashboards', 'entitiesDevices']
+ }],
+ [Authority.CUSTOMER_USER, {
+ links: ['alarms', 'dashboards', 'entitiesDevices']
+ }]
+ ]
+);
+
+interface QuickLinksWidgetSettings {
+ columns: number;
+}
+
+@Component({
+ selector: 'tb-quick-links-widget',
+ templateUrl: './quick-links-widget.component.html',
+ styleUrls: ['./home-page-widget.scss', './links-widget.component.scss']
+})
+export class QuickLinksWidgetComponent extends PageComponent implements OnInit, OnDestroy {
+
+ @Input()
+ ctx: WidgetContext;
+
+ settings: QuickLinksWidgetSettings;
+ columns: number;
+ rowHeight = '55px';
+ gutterSize = '12px';
+
+ quickLinks: QuickLinks;
+ authUser = getCurrentAuthUser(this.store);
+
+ private observeBreakpointSubscription: Subscription;
+
+ constructor(protected store: Store,
+ private cd: ChangeDetectorRef,
+ private userSettingsService: UserSettingsService,
+ private dialog: MatDialog,
+ private menuService: MenuService,
+ private breakpointObserver: BreakpointObserver) {
+ super(store);
+ }
+
+ ngOnInit() {
+ this.settings = this.ctx.settings;
+ this.columns = this.settings.columns || 3;
+ const isMdLg = this.breakpointObserver.isMatched(MediaBreakpoints['md-lg']);
+ this.rowHeight = isMdLg ? '18px' : '55px';
+ this.gutterSize = isMdLg ? '8px' : '12px';
+ this.observeBreakpointSubscription = this.breakpointObserver
+ .observe(MediaBreakpoints['md-lg'])
+ .subscribe((state: BreakpointState) => {
+ if (state.matches) {
+ this.rowHeight = '18px';
+ this.gutterSize = '8px';
+ } else {
+ this.rowHeight = '55px';
+ this.gutterSize = '12px';
+ }
+ this.cd.markForCheck();
+ }
+ );
+ this.loadQuickLinks();
+ }
+
+ ngOnDestroy() {
+ if (this.observeBreakpointSubscription) {
+ this.observeBreakpointSubscription.unsubscribe();
+ }
+ super.ngOnDestroy();
+ }
+
+ menuLinks$(): Observable> {
+ return this.quickLinks ? this.menuService.menuLinksByIds(this.quickLinks.links) : of([]);
+ }
+
+ private loadQuickLinks() {
+ this.userSettingsService.getQuickLinks().pipe(
+ map((quickLinks) => {
+ if (!quickLinks || !quickLinks.links) {
+ return defaultQuickLinksMap.get(this.authUser.authority);
+ } else {
+ return quickLinks;
+ }
+ })
+ ).subscribe(
+ (quickLinks) => {
+ this.quickLinks = quickLinks;
+ this.cd.markForCheck();
+ }
+ );
+ }
+
+ edit() {
+ /* this.dialog.open(EditDocLinksDialogComponent, {
+ disableClose: true,
+ autoFocus: false,
+ data: {
+ docLinks: this.documentationLinks
+ },
+ panelClass: ['tb-dialog', 'tb-fullscreen-dialog']
+ }).afterClosed().subscribe(
+ (result) => {
+ if (result) {
+ this.loadDocLinks();
+ }
+ }); */
+ }
+
+ addLink() {
+ /* this.dialog.open(AddDocLinkDialogComponent, {
+ disableClose: true,
+ autoFocus: false,
+ panelClass: ['tb-dialog', 'tb-fullscreen-dialog']
+ }).afterClosed().subscribe(
+ (docLink) => {
+ if (docLink) {
+ this.documentationLinks.links.push(docLink);
+ this.cd.markForCheck();
+ this.userSettingsService.updateDocumentationLinks(this.documentationLinks).subscribe();
+ }
+ }); */
+ }
+}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/toggle-header.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/toggle-header.component.html
index 3cbf61fdae..fadcadd72b 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/toggle-header.component.html
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/toggle-header.component.html
@@ -15,6 +15,13 @@
limitations under the License.
-->
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-key-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-key-settings.component.ts
index c70529bd8b..b2c88e5fcb 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-key-settings.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-key-settings.component.ts
@@ -48,6 +48,7 @@ export function flotDataKeyDefaultSettings(chartType: ChartType): TbFlotKeySetti
showLines: chartType === 'graph',
lineWidth: 1,
fillLines: false,
+ fillLinesOpacity: 0.4,
// Points settings
showPoints: false,
@@ -146,6 +147,7 @@ export class FlotKeySettingsComponent extends PageComponent implements OnInit, C
showLines: [this.chartType === 'graph', []],
lineWidth: [1, [Validators.min(0)]],
fillLines: [false, []],
+ fillLinesOpacity: [0.4, [Validators.min(0), Validators.max(1)]],
// Points settings
@@ -188,15 +190,19 @@ export class FlotKeySettingsComponent extends PageComponent implements OnInit, C
});
this.flotKeySettingsFormGroup.get('showLines').valueChanges.subscribe(() => {
- this.updateValidators(true);
+ this.updateValidators(false);
+ });
+
+ this.flotKeySettingsFormGroup.get('fillLines').valueChanges.subscribe(() => {
+ this.updateValidators(false);
});
this.flotKeySettingsFormGroup.get('showPoints').valueChanges.subscribe(() => {
- this.updateValidators(true);
+ this.updateValidators(false);
});
this.flotKeySettingsFormGroup.get('comparisonSettings.showValuesForComparison').valueChanges.subscribe(() => {
- this.updateValidators(true);
+ this.updateValidators(false);
});
this.flotKeySettingsFormGroup.valueChanges.subscribe(() => {
@@ -254,15 +260,22 @@ export class FlotKeySettingsComponent extends PageComponent implements OnInit, C
private updateValidators(emitEvent?: boolean): void {
const showLines: boolean = this.flotKeySettingsFormGroup.get('showLines').value;
+ const fillLines: boolean = this.flotKeySettingsFormGroup.get('fillLines').value;
const showPoints: boolean = this.flotKeySettingsFormGroup.get('showPoints').value;
const showValuesForComparison: boolean = this.flotKeySettingsFormGroup.get('comparisonSettings.showValuesForComparison').value;
if (showLines) {
this.flotKeySettingsFormGroup.get('lineWidth').enable({emitEvent});
this.flotKeySettingsFormGroup.get('fillLines').enable({emitEvent});
+ if (fillLines) {
+ this.flotKeySettingsFormGroup.get('fillLinesOpacity').enable({emitEvent});
+ } else {
+ this.flotKeySettingsFormGroup.get('fillLinesOpacity').disable({emitEvent});
+ }
} else {
this.flotKeySettingsFormGroup.get('lineWidth').disable({emitEvent});
this.flotKeySettingsFormGroup.get('fillLines').disable({emitEvent});
+ this.flotKeySettingsFormGroup.get('fillLinesOpacity').disable({emitEvent});
}
if (showPoints) {
@@ -287,6 +300,7 @@ export class FlotKeySettingsComponent extends PageComponent implements OnInit, C
this.flotKeySettingsFormGroup.get('lineWidth').updateValueAndValidity({emitEvent: false});
this.flotKeySettingsFormGroup.get('fillLines').updateValueAndValidity({emitEvent: false});
+ this.flotKeySettingsFormGroup.get('fillLinesOpacity').updateValueAndValidity({emitEvent: false});
this.flotKeySettingsFormGroup.get('showPointsLineWidth').updateValueAndValidity({emitEvent: false});
this.flotKeySettingsFormGroup.get('showPointsRadius').updateValueAndValidity({emitEvent: false});
this.flotKeySettingsFormGroup.get('showPointShape').updateValueAndValidity({emitEvent: false});
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/home-page/quick-links-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/home-page/quick-links-widget-settings.component.html
new file mode 100644
index 0000000000..f2936d7bba
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/home-page/quick-links-widget-settings.component.html
@@ -0,0 +1,23 @@
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/home-page/quick-links-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/home-page/quick-links-widget-settings.component.ts
new file mode 100644
index 0000000000..af22e4e06b
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/home-page/quick-links-widget-settings.component.ts
@@ -0,0 +1,52 @@
+///
+/// Copyright © 2016-2023 The Thingsboard Authors
+///
+/// Licensed under the Apache License, Version 2.0 (the "License");
+/// you may not use this file except in compliance with the License.
+/// You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing, software
+/// distributed under the License is distributed on an "AS IS" BASIS,
+/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+/// See the License for the specific language governing permissions and
+/// limitations under the License.
+///
+
+import { Component } from '@angular/core';
+import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models';
+import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
+import { Store } from '@ngrx/store';
+import { AppState } from '@core/core.state';
+
+@Component({
+ selector: 'tb-quick-links-widget-settings',
+ templateUrl: './quick-links-widget-settings.component.html',
+ styleUrls: ['./../widget-settings.scss']
+})
+export class QuickLinksWidgetSettingsComponent extends WidgetSettingsComponent {
+
+ quickLinksWidgetSettingsForm: UntypedFormGroup;
+
+ constructor(protected store: Store,
+ private fb: UntypedFormBuilder) {
+ super(store);
+ }
+
+ protected settingsForm(): UntypedFormGroup {
+ return this.quickLinksWidgetSettingsForm;
+ }
+
+ protected defaultSettings(): WidgetSettings {
+ return {
+ columns: 3
+ };
+ }
+
+ protected onSettingsSet(settings: WidgetSettings) {
+ this.quickLinksWidgetSettingsForm = this.fb.group({
+ columns: [settings.columns, [Validators.required, Validators.min(1), Validators.max(20)]]
+ });
+ }
+}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts
index 73ff1433a9..11aaf06a93 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts
@@ -262,6 +262,9 @@ import {
import {
DocLinksWidgetSettingsComponent
} from '@home/components/widget/lib/settings/home-page/doc-links-widget-settings.component';
+import {
+ QuickLinksWidgetSettingsComponent
+} from '@home/components/widget/lib/settings/home-page/quick-links-widget-settings.component';
@NgModule({
declarations: [
@@ -361,7 +364,8 @@ import {
MapWidgetSettingsComponent,
RouteMapWidgetSettingsComponent,
TripAnimationWidgetSettingsComponent,
- DocLinksWidgetSettingsComponent
+ DocLinksWidgetSettingsComponent,
+ QuickLinksWidgetSettingsComponent
],
imports: [
CommonModule,
@@ -465,7 +469,8 @@ import {
MapWidgetSettingsComponent,
RouteMapWidgetSettingsComponent,
TripAnimationWidgetSettingsComponent,
- DocLinksWidgetSettingsComponent
+ DocLinksWidgetSettingsComponent,
+ QuickLinksWidgetSettingsComponent
]
})
export class WidgetSettingsModule {
@@ -533,5 +538,6 @@ export const widgetSettingsComponentsMap: {[key: string]: Type\n \n
{{ 'widgets.activity.title' | translate }}
\n
\n {{ 'device.devices' | translate }}\n {{ 'widgets.transport-messages.title' | translate }}\n \n
\n \n \n \n \n \n \n \n \n",
+ "markdownTextPattern": "
\n
\n
{{ 'widgets.activity.title' | translate }}
\n
\n \n
\n
\n \n \n \n \n \n \n \n
",
"applyDefaultMarkdownStyle": false,
"markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n}\n"
},
@@ -308,7 +308,7 @@
"filterId": null,
"dataKeys": [
{
- "name": "activeDevicesCount",
+ "name": "activeDevicesCountHourly",
"type": "timeseries",
"label": "{i18n:device.devices}",
"color": "#305680",
@@ -320,11 +320,8 @@
"showLines": true,
"lineWidth": 3,
"fillLines": true,
+ "fillLinesOpacity": 0.04,
"showPoints": false,
- "showPointsLineWidth": 5,
- "showPointsRadius": 3,
- "showPointShape": "circle",
- "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
"showSeparateAxis": false,
"axisPosition": "left",
"comparisonSettings": {
@@ -431,7 +428,7 @@
"widgetStyle": {
"padding": "0"
},
- "widgetCss": ".tb-widget-container > .tb-widget {\n border: none !important;\n border-radius: 0 !important;\n box-shadow: none !important;\n}",
+ "widgetCss": ".tb-widget-container > .tb-widget {\n border: none !important;\n border-radius: 0 !important;\n box-shadow: none !important;\n}\n",
"pageSize": 1024,
"noDataDisplayMessage": "",
"showLegend": false,
@@ -449,6 +446,68 @@
"row": 0,
"col": 0,
"id": "d26e5cd7-75ef-d475-00c7-1a2d1114efe8"
+ },
+ "ebbd0a6e-8a47-e770-5086-7f4974250f2d": {
+ "isSystemType": true,
+ "bundleAlias": "home_page_widgets",
+ "typeAlias": "usage_info",
+ "type": "static",
+ "title": "New widget",
+ "image": null,
+ "description": null,
+ "sizeX": 7.5,
+ "sizeY": 6.5,
+ "config": {
+ "showTitle": false,
+ "backgroundColor": "rgb(255, 255, 255)",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "16px",
+ "settings": {},
+ "title": "New Usage info",
+ "dropShadow": false,
+ "enableFullscreen": false,
+ "widgetStyle": {},
+ "widgetCss": "",
+ "pageSize": 1024,
+ "noDataDisplayMessage": "",
+ "showLegend": false,
+ "datasources": []
+ },
+ "row": 0,
+ "col": 0,
+ "id": "ebbd0a6e-8a47-e770-5086-7f4974250f2d"
+ },
+ "9e3ef045-d8bc-1640-a3f4-2dd10b19d50e": {
+ "isSystemType": true,
+ "bundleAlias": "home_page_widgets",
+ "typeAlias": "quick_links",
+ "type": "static",
+ "title": "New widget",
+ "image": null,
+ "description": null,
+ "sizeX": 7.5,
+ "sizeY": 3,
+ "config": {
+ "showTitle": false,
+ "backgroundColor": "rgb(255, 255, 255)",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "16px",
+ "settings": {
+ "columns": 2
+ },
+ "title": "New Quick links",
+ "dropShadow": false,
+ "enableFullscreen": false,
+ "widgetStyle": {},
+ "widgetCss": "",
+ "pageSize": 1024,
+ "noDataDisplayMessage": "",
+ "showLegend": false,
+ "datasources": []
+ },
+ "row": 0,
+ "col": 0,
+ "id": "9e3ef045-d8bc-1640-a3f4-2dd10b19d50e"
}
},
"states": {
@@ -467,10 +526,10 @@
"mobileHeight": 8
},
"867f82cf-ecf2-2d5c-35cb-08c6f2edc3a4": {
- "sizeX": 31,
+ "sizeX": 29,
"sizeY": 16,
"row": 42,
- "col": 26,
+ "col": 28,
"mobileHide": true
},
"a23185ad-dc46-806c-0e50-5b21fb080ace": {
@@ -479,6 +538,18 @@
"row": 0,
"col": 85,
"mobileHide": true
+ },
+ "ebbd0a6e-8a47-e770-5086-7f4974250f2d": {
+ "sizeX": 28,
+ "sizeY": 16,
+ "row": 42,
+ "col": 57
+ },
+ "9e3ef045-d8bc-1640-a3f4-2dd10b19d50e": {
+ "sizeX": 28,
+ "sizeY": 16,
+ "row": 42,
+ "col": 0
}
},
"gridSettings": {
diff --git a/ui-ngx/src/app/shared/models/public-api.ts b/ui-ngx/src/app/shared/models/public-api.ts
index 1629c656da..dbc5e11213 100644
--- a/ui-ngx/src/app/shared/models/public-api.ts
+++ b/ui-ngx/src/app/shared/models/public-api.ts
@@ -56,3 +56,4 @@ export * from './user-settings.models';
export * from './widget.models';
export * from './widgets-bundle.model';
export * from './window-message.model';
+export * from './usage.models';
diff --git a/ui-ngx/src/app/shared/models/usage.models.ts b/ui-ngx/src/app/shared/models/usage.models.ts
new file mode 100644
index 0000000000..7455aadb88
--- /dev/null
+++ b/ui-ngx/src/app/shared/models/usage.models.ts
@@ -0,0 +1,39 @@
+///
+/// Copyright © 2016-2023 The Thingsboard Authors
+///
+/// Licensed under the Apache License, Version 2.0 (the "License");
+/// you may not use this file except in compliance with the License.
+/// You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing, software
+/// distributed under the License is distributed on an "AS IS" BASIS,
+/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+/// See the License for the specific language governing permissions and
+/// limitations under the License.
+///
+
+export interface UsageInfo {
+ devices: number;
+ maxDevices: number;
+ assets: number;
+ maxAssets: number;
+ customers: number;
+ maxCustomers: number;
+ users: number;
+ maxUsers: number;
+ dashboards: number;
+ maxDashboards: number;
+
+ transportMessages: number;
+ maxTransportMessages: number;
+ jsExecutions: number;
+ maxJsExecutions: number;
+ emails: number;
+ maxEmails: number;
+ sms: number;
+ maxSms: number;
+ alarms: number;
+ maxAlarms: number;
+}
diff --git a/ui-ngx/src/app/shared/models/user-settings.models.ts b/ui-ngx/src/app/shared/models/user-settings.models.ts
index 5e902a9973..b20b0b07e8 100644
--- a/ui-ngx/src/app/shared/models/user-settings.models.ts
+++ b/ui-ngx/src/app/shared/models/user-settings.models.ts
@@ -40,6 +40,10 @@ export interface DocumentationLinks {
links?: DocumentationLink[];
}
+export interface QuickLinks {
+ links?: string[];
+}
+
export interface GettingStarted {
maxSelectedIndex?: number;
lastSelectedIndex?: number;
diff --git a/ui-ngx/src/app/shared/pipe/short-number.pipe.ts b/ui-ngx/src/app/shared/pipe/short-number.pipe.ts
new file mode 100644
index 0000000000..d89bc21b4a
--- /dev/null
+++ b/ui-ngx/src/app/shared/pipe/short-number.pipe.ts
@@ -0,0 +1,53 @@
+///
+/// Copyright © 2016-2023 The Thingsboard Authors
+///
+/// Licensed under the Apache License, Version 2.0 (the "License");
+/// you may not use this file except in compliance with the License.
+/// You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing, software
+/// distributed under the License is distributed on an "AS IS" BASIS,
+/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+/// See the License for the specific language governing permissions and
+/// limitations under the License.
+///
+
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+ name: 'shortNumber'
+})
+export class ShortNumberPipe implements PipeTransform {
+
+ transform(number: number, args?: any): any {
+ if (isNaN(number)) return 0;
+ if (number === null) return 0;
+ if (number === 0) return 0;
+ let abs = Math.abs(number);
+ const rounder = Math.pow(10, 1);
+ const isNegative = number < 0;
+ const isLong = args && args.long;
+ let key = '';
+
+ const powers = [
+ {key: 'Q', longKey: ' quadrillion', value: Math.pow(10, 15)},
+ {key: 'T', longKey: ' trillion', value: Math.pow(10, 12)},
+ {key: 'B', longKey: ' billion', value: Math.pow(10, 9)},
+ {key: 'M', longKey: ' million', value: Math.pow(10, 6)},
+ {key: 'K', longKey: ' thousand', value: 1000}
+ ];
+
+ for (let i = 0; i < powers.length; i++) {
+ let reduced = abs / powers[i].value;
+ reduced = Math.round(reduced * rounder) / rounder;
+ if (reduced >= 1) {
+ abs = reduced;
+ key = isLong ? powers[i].longKey : powers[i].key;
+ break;
+ }
+ }
+ return (isNegative ? '-' : '') + abs + key;
+ }
+}
diff --git a/ui-ngx/src/app/shared/shared.module.ts b/ui-ngx/src/app/shared/shared.module.ts
index e548ff14c4..a67a4c68ea 100644
--- a/ui-ngx/src/app/shared/shared.module.ts
+++ b/ui-ngx/src/app/shared/shared.module.ts
@@ -187,6 +187,7 @@ import {
GtMdLgLayoutGapDirective,
GtMdLgShowHideDirective
} from '@shared/layout/layout.directives';
+import { ShortNumberPipe } from '@shared/pipe/short-number.pipe';
export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) {
return markedOptionsService;
@@ -203,7 +204,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
FileSizePipe,
DateAgoPipe,
SafePipe,
- DateAgoPipe,
+ ShortNumberPipe,
{
provide: FlowInjectionToken,
useValue: Flow
@@ -327,6 +328,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
FileSizePipe,
DateAgoPipe,
SafePipe,
+ ShortNumberPipe,
SelectableColumnsPipe,
KeyboardShortcutPipe,
TbJsonToStringDirective,
@@ -345,7 +347,6 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
NotificationComponent,
TemplateAutocompleteComponent,
SlackConversationAutocompleteComponent,
- DateAgoPipe,
MdLgLayoutDirective,
MdLgLayoutAlignDirective,
MdLgLayoutGapDirective,
@@ -550,6 +551,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
FileSizePipe,
DateAgoPipe,
SafePipe,
+ ShortNumberPipe,
SelectableColumnsPipe,
RouterModule,
TranslateModule,
@@ -568,7 +570,6 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
NotificationComponent,
TemplateAutocompleteComponent,
SlackConversationAutocompleteComponent,
- DateAgoPipe,
MdLgLayoutDirective,
MdLgLayoutAlignDirective,
MdLgLayoutGapDirective,
diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json
index 0c318d4849..e9017be3a3 100644
--- a/ui-ngx/src/assets/locale/locale.constant-en_US.json
+++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json
@@ -4170,6 +4170,7 @@
"line-settings": "Line settings",
"show-line": "Show line",
"fill-line": "Fill line",
+ "fill-line-opacity": "Fill opacity",
"points-settings": "Points settings",
"show-points": "Show points",
"points-line-width": "Line width of points",
@@ -5127,6 +5128,11 @@
"link-required": "Link is required.",
"columns": "Columns"
},
+ "quick-links": {
+ "title": "Quick links",
+ "add-link": "Add link",
+ "columns": "Columns"
+ },
"configured-features": {
"title": "Configured features",
"info": "Status of features that require configuration",
@@ -5148,6 +5154,11 @@
"upgrade": "Upgrade",
"version-is-up-to-date": "Version is up to date"
},
+ "usage-info": {
+ "title": "Usage",
+ "entities": "Entities",
+ "api-calls": "API calls"
+ },
"functions": {
"title": "Functions",
"pe-feature-tooltip": "Only on ThingsBoard\nProfessional Edition",