UI: Sys Admin home page

This commit is contained in:
Igor Kulikov 2023-04-05 16:31:39 +03:00
parent f10113f20c
commit 92b5b2ee85
15 changed files with 607 additions and 49 deletions

View File

@ -26,6 +26,7 @@ import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.FeaturesInfo; import org.thingsboard.server.common.data.FeaturesInfo;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.SystemInfo; import org.thingsboard.server.common.data.SystemInfo;
import org.thingsboard.server.common.data.SystemInfoData; import org.thingsboard.server.common.data.SystemInfoData;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
@ -145,9 +146,7 @@ public class DefaultSystemInfoService extends TbApplicationEventListener<Partiti
AdminSettings mailSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail"); AdminSettings mailSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail");
if (mailSettings != null) { if (mailSettings != null) {
JsonNode mailFrom = mailSettings.getJsonValue().get("mailFrom"); JsonNode mailFrom = mailSettings.getJsonValue().get("mailFrom");
if (mailFrom != null) { return StringUtils.isNotEmpty(mailFrom.asText());
return DataValidator.doValidateEmail(mailFrom.asText());
}
} }
return false; return false;
} }

View File

@ -78,7 +78,9 @@ public class DefaultUpdateService implements UpdateService {
@PostConstruct @PostConstruct
private void init() { private void init() {
version = buildProperties != null ? buildProperties.getVersion() : "unknown"; version = buildProperties != null ? buildProperties.getVersion() : "unknown";
updateMessage = new UpdateMessage(false, version, "", ""); updateMessage = new UpdateMessage(false, version, "", "",
"https://thingsboard.io/docs/reference/releases",
"https://thingsboard.io/docs/reference/releases");
if (updatesEnabled) { if (updatesEnabled) {
try { try {
platform = System.getProperty("platform", "unknown"); platform = System.getProperty("platform", "unknown");
@ -135,7 +137,9 @@ public class DefaultUpdateService implements UpdateService {
response.get("updateAvailable").asBoolean(), response.get("updateAvailable").asBoolean(),
version, version,
"3.5.0", "3.5.0",
"https://thingsboard.io/docs/user-guide/install/pe/upgrade-instructions" "https://thingsboard.io/docs/user-guide/install/upgrade-instructions",
"https://thingsboard.io/docs/reference/releases",
"https://thingsboard.io/docs/reference/releases"
); );
if (updateMessage.isUpdateAvailable() && !updateMessage.equals(prevUpdateMessage)) { if (updateMessage.isUpdateAvailable() && !updateMessage.equals(prevUpdateMessage)) {
notificationRuleProcessingService.process(TenantId.SYS_TENANT_ID, NewPlatformVersionTrigger.builder() notificationRuleProcessingService.process(TenantId.SYS_TENANT_ID, NewPlatformVersionTrigger.builder()

View File

@ -24,13 +24,16 @@ import lombok.Data;
public class UpdateMessage { public class UpdateMessage {
@ApiModelProperty(position = 1, value = "'True' if new platform update is available.") @ApiModelProperty(position = 1, value = "'True' if new platform update is available.")
private final boolean isUpdateAvailable; private final boolean updateAvailable;
@ApiModelProperty(position = 2, value = "Current ThingsBoard version.") @ApiModelProperty(position = 2, value = "Current ThingsBoard version.")
private final String currentVersion; private final String currentVersion;
@ApiModelProperty(position = 3, value = "Latest ThingsBoard version.") @ApiModelProperty(position = 3, value = "Latest ThingsBoard version.")
private final String latestVersion; private final String latestVersion;
@ApiModelProperty(position = 4, value = "Upgrade instructions URL.") @ApiModelProperty(position = 4, value = "Upgrade instructions URL.")
private final String upgradeInstructionsUrl; private final String upgradeInstructionsUrl;
@ApiModelProperty(position = 5, value = "Current ThingsBoard version release notes URL.")
private final String currentVersionReleaseNotesUrl;
@ApiModelProperty(position = 6, value = "Latest ThingsBoard version release notes URL.")
private final String latestVersionReleaseNotesUrl;
} }

View File

@ -297,16 +297,6 @@ export class AuthService {
} else if (authState.authUser.isPublic) { } else if (authState.authUser.isPublic) {
result = this.router.parseUrl(`dashboard/${authState.lastPublicDashboardId}`); result = this.router.parseUrl(`dashboard/${authState.lastPublicDashboardId}`);
} }
} else if (authState.authUser.authority === Authority.SYS_ADMIN) {
this.adminService.checkUpdates().subscribe((updateMessage) => {
if (updateMessage && updateMessage.updateAvailable) {
this.store.dispatch(new ActionNotificationShow(
{message: updateMessage.message,
type: 'info',
verticalPosition: 'bottom',
horizontalPosition: 'right'}));
}
});
} }
} }
} else { } else {

View File

@ -21,6 +21,7 @@ import { HttpClient } from '@angular/common/http';
import { import {
AdminSettings, AdminSettings,
AutoCommitSettings, AutoCommitSettings,
FeaturesInfo,
JwtSettings, JwtSettings,
MailServerSettings, MailServerSettings,
RepositorySettings, RepositorySettings,
@ -131,4 +132,8 @@ export class AdminService {
public checkUpdates(config?: RequestConfig): Observable<UpdateMessage> { public checkUpdates(config?: RequestConfig): Observable<UpdateMessage> {
return this.http.get<UpdateMessage>(`/api/admin/updates`, defaultHttpOptionsFromConfig(config)); return this.http.get<UpdateMessage>(`/api/admin/updates`, defaultHttpOptionsFromConfig(config));
} }
public getFeaturesInfo(config?: RequestConfig): Observable<FeaturesInfo> {
return this.http.get<FeaturesInfo>('/api/admin/featuresInfo', defaultHttpOptionsFromConfig(config));
}
} }

View File

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { AfterViewInit, ChangeDetectionStrategy, Component, NgZone, OnInit, ViewChild } from '@angular/core'; import { AfterViewInit, Component, NgZone, OnInit, ViewChild } from '@angular/core';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
@ -44,8 +44,7 @@ export interface SystemInfoData {
@Component({ @Component({
selector: 'tb-cluster-info-table', selector: 'tb-cluster-info-table',
templateUrl: './cluster-info-table.component.html', templateUrl: './cluster-info-table.component.html',
styleUrls: ['./cluster-info-table.component.scss'], styleUrls: ['./cluster-info-table.component.scss']
changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class ClusterInfoTableComponent extends PageComponent implements OnInit, AfterViewInit { export class ClusterInfoTableComponent extends PageComponent implements OnInit, AfterViewInit {

View File

@ -0,0 +1,66 @@
<!--
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.
-->
<div class="tb-card-content" fxLayout="column" fxLayoutGap="8px">
<div class="tb-title">Configured features
<mat-icon
matTooltip="Status of features that require configuration"
matTooltipPosition="above"
class="tb-info-icon">info</mat-icon>
</div>
<div fxLayout="column" fxFlex fxLayoutGap="12px">
<div fxLayout="row" fxFlex fxLayoutGap="12px">
<a class="tb-feature-button" fxFlex
[matTooltip]="featureTooltip(featuresInfo?.emailEnabled)"
matTooltipPosition="above"
[ngClass]="{'configured': featuresInfo?.emailEnabled}"
routerLink="/settings/outgoing-mail">
<div class="tb-feature-text">Email</div>
</a>
<a class="tb-feature-button" fxFlex
[matTooltip]="featureTooltip(featuresInfo?.smsEnabled)"
matTooltipPosition="above"
[ngClass]="{'configured': featuresInfo?.smsEnabled}"
routerLink="/settings/notifications">
<div class="tb-feature-text">SMS</div>
</a>
</div>
<div fxLayout="row" fxFlex fxLayoutGap="12px">
<a class="tb-feature-button" fxFlex
[matTooltip]="featureTooltip(featuresInfo?.notificationEnabled)"
matTooltipPosition="above"
[ngClass]="{'configured': featuresInfo?.notificationEnabled}"
routerLink="/settings/notifications">
<div class="tb-feature-text">Slack</div>
</a>
<a class="tb-feature-button" fxFlex
[matTooltip]="featureTooltip(featuresInfo?.oauthEnabled)"
matTooltipPosition="above"
[ngClass]="{'configured': featuresInfo?.oauthEnabled}"
routerLink="/security-settings/oauth2">
<div class="tb-feature-text">OAuth 2</div>
</a>
<a class="tb-feature-button" fxFlex
[matTooltip]="featureTooltip(featuresInfo?.twoFaEnabled)"
matTooltipPosition="above"
[ngClass]="{'configured': featuresInfo?.twoFaEnabled}"
routerLink="/security-settings/2fa">
<div class="tb-feature-text">2FA</div>
</a>
</div>
</div>
</div>

View File

@ -0,0 +1,85 @@
/**
* 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.
*/
: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);
}
.tb-info-icon {
color: rgba(0, 0, 0, 0.12);
font-size: 16px;
width: 16px;
height: 16px;
line-height: 15px;
vertical-align: middle;
}
.tb-feature-button {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
padding: 12px;
background: #FFFFFF;
border: 1px solid rgba(0, 0, 0, 0.05);
box-shadow: 0 5px 16px rgba(0, 0, 0, 0.04);
border-radius: 10px;
&:hover {
border: 1px solid rgba(0, 0, 0, 0.12);
box-shadow: 0 4px 10px rgba(23, 33, 90, 0.08);
}
.tb-feature-text {
font-weight: 400;
font-size: 14px;
line-height: 20px;
color: rgba(0, 0, 0, 0.87);
&:before {
content: "close";
font-family: 'Material Icons Round';
font-size: 20px;
line-height: 1;
color: rgba(0, 0, 0, 0.12);
font-weight: 600;
position: relative;
background: #F4F4F4;
border-radius: 4px;
width: 24px;
height: 24px;
margin-right: 8px;
vertical-align: bottom;
}
}
&.configured {
.tb-feature-text {
&:before {
content: "check";
color: #198038;
background: #F3F6FA;
}
}
}
}
}

View File

@ -0,0 +1,60 @@
///
/// 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, OnInit } from '@angular/core';
import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { AdminService } from '@core/http/admin.service';
import { FeaturesInfo } from '@shared/models/settings.models';
import { getCurrentAuthUser } from '@core/auth/auth.selectors';
import { Authority } from '@shared/models/authority.enum';
import { of } from 'rxjs';
@Component({
selector: 'tb-configured-features',
templateUrl: './configured-features.component.html',
styleUrls: ['./configured-features.component.scss']
})
export class ConfiguredFeaturesComponent extends PageComponent implements OnInit {
authUser = getCurrentAuthUser(this.store);
featuresInfo: FeaturesInfo;
constructor(protected store: Store<AppState>,
private cd: ChangeDetectorRef,
private adminService: AdminService) {
super(store);
}
ngOnInit() {
(this.authUser.authority === Authority.SYS_ADMIN ?
this.adminService.getFeaturesInfo() : of(null)).subscribe(
(featuresInfo) => {
this.featuresInfo = featuresInfo;
this.cd.markForCheck();
}
);
}
featureTooltip(configured: boolean): string {
if (configured) {
return 'Feature is configured.\nClick to setup';
} else {
return 'Feature is not configured.\nClick to setup';
}
}
}

View File

@ -18,18 +18,24 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { SharedModule } from '@app/shared/shared.module'; import { SharedModule } from '@app/shared/shared.module';
import { ClusterInfoTableComponent } from '@home/components/widget/lib/home-page/cluster-info-table.component'; import { ClusterInfoTableComponent } from '@home/components/widget/lib/home-page/cluster-info-table.component';
import { ConfiguredFeaturesComponent } from '@home/components/widget/lib/home-page/configured-features.component';
import { VersionInfoComponent } from '@home/components/widget/lib/home-page/version-info.component';
@NgModule({ @NgModule({
declarations: declarations:
[ [
ClusterInfoTableComponent ClusterInfoTableComponent,
ConfiguredFeaturesComponent,
VersionInfoComponent
], ],
imports: [ imports: [
CommonModule, CommonModule,
SharedModule SharedModule
], ],
exports: [ exports: [
ClusterInfoTableComponent ClusterInfoTableComponent,
ConfiguredFeaturesComponent,
VersionInfoComponent
] ]
}) })
export class HomePageWidgetsModule { } export class HomePageWidgetsModule { }

View File

@ -0,0 +1,44 @@
<!--
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.
-->
<div class="tb-card-content" fxLayout="column" fxLayoutGap="8px">
<div class="tb-card-header">
<div class="tb-title">Version</div>
<a mat-button color="primary" href="https://thingsboard.io/docs/contact-us/" target="_blank">Contact us</a>
</div>
<div fxLayout="column" fxFlex fxLayoutGap="12px">
<div class="tb-version-info-container" fxFlex>
<div class="tb-card-header">
<a class="tb-version-link" [href]="updateMessage?.currentVersionReleaseNotesUrl" target="_blank">Current version</a>
</div>
<div class="tb-version">{{ updateMessage?.currentVersion }}</div>
</div>
<div class="tb-version-info-container" fxFlex *ngIf="updateMessage?.updateAvailable; else versionUpToDate">
<div class="tb-card-header">
<a class="tb-version-link" [href]="updateMessage?.latestVersionReleaseNotesUrl" target="_blank">Available version</a>
<a mat-flat-button color="primary" [href]="updateMessage?.upgradeInstructionsUrl" target="_blank">Upgrade</a>
</div>
<div class="tb-version">{{ updateMessage?.latestVersion }}</div>
</div>
</div>
</div>
<ng-template #versionUpToDate>
<div class="tb-version-info-container up-to-date" fxFlex>
<div class="material-icons-round check-icon">check</div>
<div class="up-to-date-text">Version is up to date</div>
</div>
</ng-template>

View File

@ -0,0 +1,102 @@
/**
* 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.
*/
:host {
.tb-card-content {
width: 100%;
height: 100%;
}
.tb-card-header {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.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);
}
.tb-version-info-container {
border: 1px solid rgba(0, 0, 0, 0.05);
box-shadow: 0 5px 16px rgba(0, 0, 0, 0.04);
border-radius: 10px;
padding: 16px;
display: flex;
flex-direction: column;
justify-content: space-evenly;
&.up-to-date {
box-shadow: none;
border: none;
align-items: center;
background: #F3F6FA;
}
}
.tb-version-link {
font-weight: 400;
font-size: 14px;
line-height: 20px;
letter-spacing: 0.2px;
color: rgba(0, 0, 0, 0.76);
position: relative;
border-bottom: none;
&:hover, &:focus {
border-bottom: none;
}
&::after {
content: 'arrow_forward';
display: inline-block;
position: absolute;
top: -2px;
right: -28px;
transform: rotate(315deg);
font-family: 'Material Icons';
font-weight: normal;
font-style: normal;
font-size: 18px;
color: rgba(0, 0, 0, 0.12);
}
&:hover::after {
color: inherit;
}
}
.tb-version {
font-weight: 500;
font-size: 18px;
line-height: 24px;
letter-spacing: 0.15px;
color: rgba(0, 0, 0, 0.87);
}
.check-icon {
color: #198038;
font-weight: 600;
}
.up-to-date-text {
font-weight: 400;
font-size: 14px;
line-height: 20px;
letter-spacing: 0.2px;
color: rgba(0, 0, 0, 0.76);
}
}

View File

@ -0,0 +1,60 @@
///
/// 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, OnInit } from '@angular/core';
import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { AdminService } from '@core/http/admin.service';
import { UpdateMessage } from '@shared/models/settings.models';
import { getCurrentAuthUser } from '@core/auth/auth.selectors';
import { Authority } from '@shared/models/authority.enum';
import { of } from 'rxjs';
@Component({
selector: 'tb-version-info',
templateUrl: './version-info.component.html',
styleUrls: ['./version-info.component.scss']
})
export class VersionInfoComponent extends PageComponent implements OnInit {
authUser = getCurrentAuthUser(this.store);
updateMessage: UpdateMessage;
constructor(protected store: Store<AppState>,
private cd: ChangeDetectorRef,
private adminService: AdminService) {
super(store);
}
ngOnInit() {
(this.authUser.authority === Authority.SYS_ADMIN ?
this.adminService.checkUpdates() : of(null)).subscribe(
(updateMessage) => {
this.updateMessage = updateMessage;
this.cd.markForCheck();
}
);
}
featureTooltip(configured: boolean): string {
if (configured) {
return 'Feature is configured.\nClick to setup';
} else {
return 'Feature is not configured.\nClick to setup';
}
}
}

View File

@ -1561,30 +1561,7 @@
"sizeX": 5, "sizeX": 5,
"sizeY": 3.5, "sizeY": 3.5,
"config": { "config": {
"datasources": [ "datasources": [],
{
"type": "entityCount",
"name": null,
"entityAliasId": "ae870700-071f-b3bc-406c-16ba554c5a55",
"filterId": null,
"dataKeys": [
{
"name": "count",
"type": "count",
"label": "tenantProfilesCount",
"color": "#2196f3",
"settings": {},
"_hash": 0.8491768696709192,
"aggregationType": null,
"units": null,
"decimals": null,
"funcBody": null,
"usePostProcessing": null,
"postFuncBody": null
}
]
}
],
"timewindow": { "timewindow": {
"displayValue": "", "displayValue": "",
"selectedTab": 0, "selectedTab": 0,
@ -1640,6 +1617,140 @@
"row": 0, "row": 0,
"col": 0, "col": 0,
"id": "8acbf5df-f9fc-114d-216f-86f081aa4779" "id": "8acbf5df-f9fc-114d-216f-86f081aa4779"
},
"d2784f6c-0518-fd95-1d28-b21f70bdcb10": {
"isSystemType": true,
"bundleAlias": "cards",
"typeAlias": "markdown_card",
"type": "latest",
"title": "New widget",
"image": null,
"description": null,
"sizeX": 5,
"sizeY": 3.5,
"config": {
"datasources": [],
"timewindow": {
"displayValue": "",
"selectedTab": 0,
"realtime": {
"realtimeType": 1,
"interval": 1000,
"timewindowMs": 60000,
"quickInterval": "CURRENT_DAY"
},
"history": {
"historyType": 0,
"interval": 1000,
"timewindowMs": 60000,
"fixedTimewindow": {
"startTimeMs": 1680168340431,
"endTimeMs": 1680254740431
},
"quickInterval": "CURRENT_DAY"
},
"aggregation": {
"type": "AVG",
"limit": 25000
}
},
"showTitle": false,
"backgroundColor": "#fff",
"color": "rgba(0, 0, 0, 0.87)",
"padding": "16px",
"settings": {
"useMarkdownTextFunction": false,
"markdownTextPattern": "<div style=\"height: 100%;\">\n <tb-configured-features></tb-configured-features>\n</div>",
"applyDefaultMarkdownStyle": false
},
"title": "Configured features",
"showTitleIcon": false,
"iconColor": "rgba(0, 0, 0, 0.87)",
"iconSize": "24px",
"titleTooltip": "",
"dropShadow": false,
"enableFullscreen": false,
"widgetStyle": {},
"titleStyle": {
"fontSize": "16px",
"fontWeight": 400
},
"showLegend": false,
"useDashboardTimewindow": true,
"widgetCss": "",
"pageSize": 1024,
"noDataDisplayMessage": ""
},
"row": 0,
"col": 0,
"id": "d2784f6c-0518-fd95-1d28-b21f70bdcb10"
},
"66dcf1a2-9d83-6873-c693-d6a5d989b3f8": {
"isSystemType": true,
"bundleAlias": "cards",
"typeAlias": "markdown_card",
"type": "latest",
"title": "New widget",
"image": null,
"description": null,
"sizeX": 5,
"sizeY": 3.5,
"config": {
"datasources": [],
"timewindow": {
"displayValue": "",
"selectedTab": 0,
"realtime": {
"realtimeType": 1,
"interval": 1000,
"timewindowMs": 60000,
"quickInterval": "CURRENT_DAY"
},
"history": {
"historyType": 0,
"interval": 1000,
"timewindowMs": 60000,
"fixedTimewindow": {
"startTimeMs": 1680168340431,
"endTimeMs": 1680254740431
},
"quickInterval": "CURRENT_DAY"
},
"aggregation": {
"type": "AVG",
"limit": 25000
}
},
"showTitle": false,
"backgroundColor": "#fff",
"color": "rgba(0, 0, 0, 0.87)",
"padding": "16px",
"settings": {
"useMarkdownTextFunction": false,
"markdownTextPattern": "<div style=\"height: 100%;\">\n <tb-version-info></tb-version-info>\n</div>",
"applyDefaultMarkdownStyle": false
},
"title": "Version",
"showTitleIcon": false,
"iconColor": "rgba(0, 0, 0, 0.87)",
"iconSize": "24px",
"titleTooltip": "",
"dropShadow": false,
"enableFullscreen": false,
"widgetStyle": {},
"titleStyle": {
"fontSize": "16px",
"fontWeight": 400
},
"showLegend": false,
"useDashboardTimewindow": true,
"widgetCss": "",
"pageSize": 1024,
"noDataDisplayMessage": ""
},
"row": 0,
"col": 0,
"id": "66dcf1a2-9d83-6873-c693-d6a5d989b3f8"
} }
}, },
"states": { "states": {
@ -1708,6 +1819,18 @@
"sizeY": 7, "sizeY": 7,
"row": 19, "row": 19,
"col": 11 "col": 11
},
"d2784f6c-0518-fd95-1d28-b21f70bdcb10": {
"sizeX": 20,
"sizeY": 7,
"row": 12,
"col": 0
},
"66dcf1a2-9d83-6873-c693-d6a5d989b3f8": {
"sizeX": 11,
"sizeY": 7,
"row": 19,
"col": 0
} }
}, },
"gridSettings": { "gridSettings": {
@ -1810,7 +1933,7 @@
"filter": { "filter": {
"type": "entityType", "type": "entityType",
"resolveMultiple": true, "resolveMultiple": true,
"entityType": "TENANT" "entityType": "TENANT_PROFILE"
} }
}, },
"a1ddb8fa-90ff-5598-e7f2-e254194d055d": { "a1ddb8fa-90ff-5598-e7f2-e254194d055d": {
@ -1908,4 +2031,4 @@
}, },
"externalId": null, "externalId": null,
"name": "System Administrator Home Page" "name": "System Administrator Home Page"
} }

View File

@ -71,8 +71,12 @@ export interface JwtSettings {
} }
export interface UpdateMessage { export interface UpdateMessage {
message: string;
updateAvailable: boolean; updateAvailable: boolean;
currentVersion: string;
latestVersion: string;
upgradeInstructionsUrl: string;
currentVersionReleaseNotesUrl: string;
latestVersionReleaseNotesUrl: string;
} }
export const phoneNumberPattern = /^\+[1-9]\d{1,14}$/; export const phoneNumberPattern = /^\+[1-9]\d{1,14}$/;
@ -437,3 +441,11 @@ export interface AutoVersionCreateConfig extends VersionCreateConfig {
} }
export type AutoCommitSettings = {[entityType: string]: AutoVersionCreateConfig}; export type AutoCommitSettings = {[entityType: string]: AutoVersionCreateConfig};
export interface FeaturesInfo {
emailEnabled: boolean;
smsEnabled: boolean;
notificationEnabled: boolean;
oauthEnabled: boolean;
twoFaEnabled: boolean;
}