diff --git a/ui-ngx/src/app/modules/home/pages/mobile/applications/applications.module.ts b/ui-ngx/src/app/modules/home/pages/mobile/applications/applications.module.ts
index eae2e5e278..d8e619f865 100644
--- a/ui-ngx/src/app/modules/home/pages/mobile/applications/applications.module.ts
+++ b/ui-ngx/src/app/modules/home/pages/mobile/applications/applications.module.ts
@@ -23,6 +23,10 @@ import { HomeComponentsModule } from '@home/components/home-components.module';
import { ApplicationsRoutingModule } from '@home/pages/mobile/applications/applications-routing.module';
import { ReleaseNotesPanelComponent } from '@home/pages/mobile/applications/release-notes-panel.component';
import { MobileAppDialogComponent } from '@home/pages/mobile/applications/mobile-app-dialog.component';
+import { RemoveAppDialogComponent } from '@home/pages/mobile/applications/remove-app-dialog.component';
+import {
+ MobileAppConfigurationDialogComponent
+} from '@home/pages/mobile/applications/mobile-app-configuration-dialog.component';
@NgModule({
declarations: [
@@ -30,6 +34,8 @@ import { MobileAppDialogComponent } from '@home/pages/mobile/applications/mobile
MobileAppTableHeaderComponent,
ReleaseNotesPanelComponent,
MobileAppDialogComponent,
+ RemoveAppDialogComponent,
+ MobileAppConfigurationDialogComponent,
],
imports: [
CommonModule,
diff --git a/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-configuration-dialog.component.html b/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-configuration-dialog.component.html
new file mode 100644
index 0000000000..5689e40f1e
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-configuration-dialog.component.html
@@ -0,0 +1,36 @@
+
+
+ mobile.configuration-dialog
+
+
+
+
+
+
+ {{ 'action.dont-show-again' | translate}}
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-configuration-dialog.component.scss b/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-configuration-dialog.component.scss
new file mode 100644
index 0000000000..129f5c06de
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-configuration-dialog.component.scss
@@ -0,0 +1,22 @@
+/**
+ * Copyright © 2016-2024 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 {
+ height: 100%;
+ max-height: 100vh;
+ display: grid;
+ width: 800px;
+ grid-template-rows: min-content minmax(auto, 1fr) min-content;
+}
diff --git a/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-configuration-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-configuration-dialog.component.ts
new file mode 100644
index 0000000000..9721396937
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-configuration-dialog.component.ts
@@ -0,0 +1,58 @@
+///
+/// Copyright © 2016-2024 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, Inject } from '@angular/core';
+import { DialogComponent } from '@shared/components/dialog.component';
+import { Store } from '@ngrx/store';
+import { AppState } from '@core/core.state';
+import { Router } from '@angular/router';
+import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
+
+export interface MobileAppConfigurationDialogData {
+ afterAdd: boolean;
+}
+
+@Component({
+ selector: 'tb-mobile-app-configuration-dialog',
+ templateUrl: './mobile-app-configuration-dialog.component.html',
+ styleUrls: ['./mobile-app-configuration-dialog.component.scss']
+})
+export class MobileAppConfigurationDialogComponent extends DialogComponent {
+
+ notShowAgain = false;
+
+ showDontShowAgain: boolean;
+
+ constructor(protected store: Store,
+ protected router: Router,
+ @Inject(MAT_DIALOG_DATA) private data: MobileAppConfigurationDialogData,
+ protected dialogRef: MatDialogRef,
+ ) {
+ super(store, router, dialogRef);
+
+ this.showDontShowAgain = this.data.afterAdd;
+
+ }
+
+ close(): void {
+ if (this.notShowAgain && this.showDontShowAgain) {
+ // this.store.dispatch(new ActionPreferencesPutUserSettings({ notDisplayConnectivityAfterAddDevice: true }));
+ this.dialogRef.close(null);
+ } else {
+ this.dialogRef.close(null);
+ }
+ }
+}
diff --git a/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-table-config.resolver.ts
index ef513728a9..8dd37f7c35 100644
--- a/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-table-config.resolver.ts
+++ b/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-table-config.resolver.ts
@@ -17,6 +17,7 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot } from '@angular/router';
import {
+ CellActionDescriptor,
CellActionDescriptorType,
DateEntityTableColumn,
EntityTableColumn,
@@ -29,9 +30,28 @@ import { Direction } from '@app/shared/models/page/sort-order';
import { MobileAppService } from '@core/http/mobile-app.service';
import { MobileAppComponent } from '@home/pages/mobile/applications/mobile-app.component';
import { MobileAppTableHeaderComponent } from '@home/pages/mobile/applications/mobile-app-table-header.component';
-import { MobileApp, MobileAppStatus, mobileAppStatusTranslations } from '@shared/models/mobile-app.models';
+import {
+ MobileApp,
+ MobileAppBundleInfo,
+ MobileAppStatus,
+ mobileAppStatusTranslations
+} from '@shared/models/mobile-app.models';
import { platformTypeTranslations } from '@shared/models/oauth2.models';
import { TruncatePipe } from '@shared/pipe/truncate.pipe';
+import { NotificationTemplate } from '@shared/models/notification.models';
+import { BaseData, HasId } from '@shared/models/base-data';
+import {
+ MobileBundleDialogComponent,
+ MobileBundleDialogData
+} from '@home/pages/mobile/bundes/mobile-bundle-dialog.component';
+import { MatDialog } from '@angular/material/dialog';
+import {
+ MobileAppDeleteDialogData,
+ RemoveAppDialogComponent
+} from '@home/pages/mobile/applications/remove-app-dialog.component';
+import {
+ MobileAppConfigurationDialogComponent, MobileAppConfigurationDialogData
+} from '@home/pages/mobile/applications/mobile-app-configuration-dialog.component';
@Injectable()
export class MobileAppTableConfigResolver {
@@ -41,10 +61,12 @@ export class MobileAppTableConfigResolver {
constructor(private translate: TranslateService,
private datePipe: DatePipe,
private mobileAppService: MobileAppService,
- private truncatePipe: TruncatePipe) {
+ private truncatePipe: TruncatePipe,
+ private dialog: MatDialog,) {
this.config.selectionEnabled = false;
this.config.entityType = EntityType.MOBILE_APP;
this.config.addEnabled = false;
+ this.config.entitiesDeleteEnabled = false;
this.config.rowPointer = true;
this.config.entityTranslations = entityTypeTranslations.get(EntityType.MOBILE_APP);
this.config.entityResources = entityTypeResources.get(EntityType.MOBILE_APP);
@@ -97,18 +119,73 @@ export class MobileAppTableConfigResolver {
(entity) => entity.versionInfo?.latestVersion ?? '', () => ({}), false),
);
- this.config.deleteEntityTitle = (app) => this.translate.instant('mobile.delete-applications-title', {applicationName: app.pkgName});
- this.config.deleteEntityContent = () => this.translate.instant('mobile.delete-applications-text');
this.config.entitiesFetchFunction = pageLink => this.mobileAppService.getTenantMobileAppInfos(pageLink);
this.config.loadEntity = id => this.mobileAppService.getMobileAppInfoById(id.id);
this.config.saveEntity = (mobileApp) => this.mobileAppService.saveMobileApp(mobileApp);
this.config.deleteEntity = id => this.mobileAppService.deleteMobileApp(id.id);
+
+ this.config.cellActionDescriptors = this.configureCellActions();
}
resolve(_route: ActivatedRouteSnapshot): EntityTableConfig {
return this.config;
}
+ private configureCellActions(): Array> {
+ return [
+ {
+ name: this.translate.instant('mobile.configuration-app'),
+ icon: 'code',
+ isEnabled: () => true,
+ onAction: ($event, entity) => this.configurationApp($event, entity)
+ },
+ {
+ name: this.translate.instant('action.delete'),
+ icon: 'delete',
+ isEnabled: () => true,
+ onAction: ($event, entity) => this.deleteEntity($event, entity)
+ }
+ ];
+ }
+
+ private deleteEntity($event: Event, entity: MobileApp) {
+ if ($event) {
+ $event.stopPropagation();
+ }
+ this.dialog.open(RemoveAppDialogComponent, {
+ disableClose: true,
+ panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
+ data: {
+ id: entity.id.id
+ }
+ }).afterClosed()
+ .subscribe((res) => {
+ if (res) {
+ this.config.updateData();
+ }
+ });
+ }
+
+ private configurationApp($event: Event, entity: MobileApp, afterAdd = false) {
+ if ($event) {
+ $event.stopPropagation();
+ }
+ this.dialog.open(MobileAppConfigurationDialogComponent, {
+ disableClose: true,
+ panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
+ data: {
+ afterAdd
+ }
+ }).afterClosed()
+ .subscribe(() => {
+ if (afterAdd) {
+ this.config.updateData();
+ }
+ });
+ }
+
private mobileStatus(status: MobileAppStatus): string {
const translateKey = mobileAppStatusTranslations.get(status);
let backgroundColor = 'rgba(25, 128, 56, 0.06)';
diff --git a/ui-ngx/src/app/modules/home/pages/mobile/applications/remove-app-dialog.component.html b/ui-ngx/src/app/modules/home/pages/mobile/applications/remove-app-dialog.component.html
new file mode 100644
index 0000000000..faca470cdf
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/pages/mobile/applications/remove-app-dialog.component.html
@@ -0,0 +1,48 @@
+
+
+ mobile.delete-application
+
+
+
+
+
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/pages/mobile/applications/remove-app-dialog.component.scss b/ui-ngx/src/app/modules/home/pages/mobile/applications/remove-app-dialog.component.scss
new file mode 100644
index 0000000000..de6f669e64
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/pages/mobile/applications/remove-app-dialog.component.scss
@@ -0,0 +1,24 @@
+/**
+ * Copyright © 2016-2024 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{
+ width: 750px;
+ height: 100%;
+ max-width: 100%;
+ max-height: 100vh;
+ display: grid;
+ grid-template-rows: min-content 4px minmax(auto, 1fr) min-content;
+ --mdc-outlined-text-field-outline-color: rgba(0,0,0,0.12);
+}
diff --git a/ui-ngx/src/app/modules/home/pages/mobile/applications/remove-app-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/applications/remove-app-dialog.component.ts
new file mode 100644
index 0000000000..112b78bb5c
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/pages/mobile/applications/remove-app-dialog.component.ts
@@ -0,0 +1,68 @@
+///
+/// Copyright © 2016-2024 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, Inject } from '@angular/core';
+import { DialogComponent } from '@shared/components/dialog.component';
+import { Store } from '@ngrx/store';
+import { AppState } from '@core/core.state';
+import { Router } from '@angular/router';
+import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
+import { TranslateService } from '@ngx-translate/core';
+import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
+import { FormBuilder } from '@angular/forms';
+import { MobileAppService } from '@core/http/mobile-app.service';
+
+export interface MobileAppDeleteDialogData {
+ id: string;
+}
+
+@Component({
+ selector: 'tb-remove-app-dialog',
+ templateUrl: './remove-app-dialog.component.html',
+ styleUrls: ['./remove-app-dialog.component.scss']
+})
+export class RemoveAppDialogComponent extends DialogComponent {
+
+ readonly deleteApplicationText: SafeHtml;
+ readonly deleteVerificationText: string;
+
+ deleteVerification = this.fb.control('');
+
+ constructor(protected store: Store,
+ protected router: Router,
+ protected dialogRef: MatDialogRef,
+ @Inject(MAT_DIALOG_DATA) private data: MobileAppDeleteDialogData,
+ private translate: TranslateService,
+ private sanitizer: DomSanitizer,
+ private fb: FormBuilder,
+ private mobileAppService: MobileAppService,) {
+ super(store, router, dialogRef);
+ this.deleteVerificationText = this.translate.instant('mobile.delete-application-phrase');
+ this.deleteApplicationText = this.sanitizer.bypassSecurityTrustHtml(
+ this.translate.instant('mobile.delete-application-text', {phrase: this.deleteVerificationText})
+ )
+ }
+
+ cancel(): void {
+ this.dialogRef.close(false);
+ }
+
+ delete(): void {
+ this.mobileAppService.deleteMobileApp(this.data.id).subscribe(() => {
+ this.dialogRef.close(true);
+ });
+ }
+}
diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.scss b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.scss
index 5fda87092d..641e7c877d 100644
--- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.scss
+++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.scss
@@ -14,6 +14,8 @@
* limitations under the License.
*/
:host{
+ --mdc-outlined-text-field-outline-color: rgba(0,0,0,0.12);
+
.mobile-page-form {
display: flex;
flex-direction: column;
diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/default-mobile-page-panel.component.scss b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/default-mobile-page-panel.component.scss
index a8435cb97e..875e7d8703 100644
--- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/default-mobile-page-panel.component.scss
+++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/default-mobile-page-panel.component.scss
@@ -20,6 +20,7 @@
display: flex;
flex-direction: column;
gap: 16px;
+ --mdc-outlined-text-field-outline-color: rgba(0,0,0,0.12);
@media #{$mat-lt-md} {
width: 90vw;
}
diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-dialog.component.scss b/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-dialog.component.scss
index 3649f88d5c..a93cd1026e 100644
--- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-dialog.component.scss
+++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-dialog.component.scss
@@ -22,6 +22,7 @@
max-height: 100vh;
display: grid;
grid-template-rows: min-content 4px minmax(auto, 1fr) min-content;
+ --mdc-outlined-text-field-outline-color: rgba(0,0,0,0.12);
.mat-mdc-slide-toggle {
margin-bottom: 16px;
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 2dc78fa6ab..0ec7890aed 100644
--- a/ui-ngx/src/assets/locale/locale.constant-en_US.json
+++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json
@@ -3405,8 +3405,10 @@
"copy-application-secret": "Copy application secret",
"copy-google-play-link": "Copy Google Play link",
"copy-sha256-certificate-fingerprints": "Copy SHA256 certificate fingerprints",
- "delete-applications-text": "Be careful, after the confirmation the mobile application and all related data will become unrecoverable.",
- "delete-applications-title": "Are you sure you want to delete the mobile application '{{applicationName}}'?",
+ "delete-application": "Delete application",
+ "delete-application-button-text": "I understand consequences, delete application",
+ "delete-application-text": "This action can not be undone. This will permanently delete your application.
If you don’t want to delete it permanently you can suspend application temporary.
To delete the application anyway please type \"{{phrase}}\" to confirm.",
+ "delete-application-phrase": "delete application",
"delete-applications-bundle-text": "Be careful, after the confirmation the mobile bundle and all related data will become unrecoverable.",
"delete-applications-bundle-title": "Are you sure you want to delete the mobile bundle '{{bundleName}}'?",
"generate-application-secret": "Generate application secret",
@@ -3484,7 +3486,10 @@
"edit-page": "Edit page",
"edit-custom-page": "Edit custom page",
"delete-page": "Delete page",
- "qr-code-widget": "QR code widget"
+ "qr-code-widget": "QR code widget",
+ "type-here": "Type hero",
+ "configuration-dialog": "Configuration dialog",
+ "configuration-app": "Configuration app"
},
"notification": {
"action-button": "Action button",