UI: Sys Admin home page
This commit is contained in:
parent
f10113f20c
commit
92b5b2ee85
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|
||||||
|
|||||||
@ -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>
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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 { }
|
||||||
|
|||||||
@ -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>
|
||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user