Browse Source

UI: added basic/advanced config for mobile app qr code widget, added QR code responsiveness

pull/10838/head
rusikv 2 years ago
parent
commit
e019b754a3
  1. 10
      application/src/main/data/json/system/widget_types/mobile_app_qr_code.json
  2. 10
      ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts
  3. 118
      ui-ngx/src/app/modules/home/components/widget/config/basic/cards/mobile-app-qr-code-basic-config.component.html
  4. 182
      ui-ngx/src/app/modules/home/components/widget/config/basic/cards/mobile-app-qr-code-basic-config.component.ts
  5. 39
      ui-ngx/src/app/modules/home/components/widget/lib/cards/mobile-app-qr-code-widget.models.ts
  6. 17
      ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.html
  7. 59
      ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.scss
  8. 94
      ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.ts
  9. 89
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/mobile-app-qr-code-widget-settings.component.html
  10. 41
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/mobile-app-qr-code-widget-settings.component.ts
  11. 26
      ui-ngx/src/assets/dashboard/sys_admin_home_page.json
  12. 26
      ui-ngx/src/assets/dashboard/tenant_admin_home_page.json
  13. 3
      ui-ngx/src/assets/locale/locale.constant-en_US.json

10
application/src/main/data/json/system/widget_types/mobile_app_qr_code.json

@ -7,15 +7,17 @@
"descriptor": {
"type": "static",
"sizeX": 7.5,
"sizeY": 3,
"sizeY": 4,
"resources": [],
"templateHtml": "<tb-mobile-app-qrcode-widget\n [ctx]=\"ctx\">\n</tb-mobile-app-qrcode-widget>",
"templateHtml": "<tb-mobile-app-qrcode-widget\n [ctx]=\"ctx\"\n [widgetTitlePanel]=\"widgetTitlePanel\">\n</tb-mobile-app-qrcode-widget>",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n}\n",
"controllerScript": "self.onInit = function() {\n}\n\nself.typeParameters = function() {\n return {\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true\n };\n};",
"settingsSchema": "",
"dataKeySettingsSchema": "",
"settingsDirective": "tb-mobile-app-qr-code-widget-settings",
"defaultConfig": "{\n \"showTitle\": true,\n \"backgroundColor\": \"rgb(255, 255, 255)\",\n \"color\": \"rgba(0, 0, 0, 0.87)\",\n \"padding\": \"8px\",\n \"settings\": {\n \"useSystemSettings\": true,\n \"androidConfig\": {\n \"enabled\": true\n },\n \"iosConfig\": {\n \"enabled\": true\n },\n \"qrCodeConfig\": {\n \"badgeEnabled\": true,\n \"badgeStyle\": \"ORIGINAL\",\n \"badgePosition\": \"RIGHT\",\n \"qrCodeLabelEnabled\": true,\n \"qrCodeLabel\": \"Scan to connect or download mobile app\"\n }\n },\n \"title\": \"Mobile app QR code\",\n \"dropShadow\": true,\n \"enableFullscreen\": false,\n \"widgetStyle\": {},\n \"widgetCss\": \"\",\n \"pageSize\": 1024,\n \"noDataDisplayMessage\": \"\",\n \"showTitleIcon\": false,\n \"titleTooltip\": \"\",\n \"titleStyle\": {\n \"fontSize\": \"16px\",\n \"fontWeight\": 400\n }\n}\n"
"hasBasicMode": true,
"basicModeDirective": "tb-mobile-app-qr-code-basic-config",
"defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"useSystemSettings\":true,\"qrCodeConfig\":{\"badgeEnabled\":true,\"badgePosition\":\"RIGHT\",\"qrCodeLabelEnabled\":true,\"qrCodeLabel\":\"Scan to connect or download mobile app\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Mobile app QR code\",\"dropShadow\":true,\"showTitleIcon\":false,\"titleTooltip\":\"\",\"enableFullscreen\":false,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"configMode\":\"basic\"}"
},
"tags": [
"ios",

10
ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts

@ -129,6 +129,7 @@ import {
import {
RadarChartBasicConfigComponent
} from '@home/components/widget/config/basic/chart/radar-chart-basic-config.component';
import { MobileAppQrCodeBasicConfigComponent } from '@home/components/widget/config/basic/cards/mobile-app-qr-code-basic-config.component';
@NgModule({
declarations: [
@ -171,7 +172,8 @@ import {
PieChartBasicConfigComponent,
BarChartBasicConfigComponent,
PolarAreaChartBasicConfigComponent,
RadarChartBasicConfigComponent
RadarChartBasicConfigComponent,
MobileAppQrCodeBasicConfigComponent
],
imports: [
CommonModule,
@ -216,7 +218,8 @@ import {
PieChartBasicConfigComponent,
BarChartBasicConfigComponent,
PolarAreaChartBasicConfigComponent,
RadarChartBasicConfigComponent
RadarChartBasicConfigComponent,
MobileAppQrCodeBasicConfigComponent
]
})
export class BasicWidgetConfigModule {
@ -255,5 +258,6 @@ export const basicWidgetConfigComponentsMap: {[key: string]: Type<IBasicWidgetCo
'tb-pie-chart-basic-config': PieChartBasicConfigComponent,
'tb-bar-chart-basic-config': BarChartBasicConfigComponent,
'tb-polar-area-chart-basic-config': PolarAreaChartBasicConfigComponent,
'tb-radar-chart-basic-config': RadarChartBasicConfigComponent
'tb-radar-chart-basic-config': RadarChartBasicConfigComponent,
'tb-mobile-app-qr-code-basic-config': MobileAppQrCodeBasicConfigComponent
};

118
ui-ngx/src/app/modules/home/components/widget/config/basic/cards/mobile-app-qr-code-basic-config.component.html

@ -0,0 +1,118 @@
<!--
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.
-->
<ng-container *ngIf="mobileAppQrCodeWidgetConfigForm" [formGroup]="mobileAppQrCodeWidgetConfigForm">
<div class="tb-form-panel no-border no-padding">
<div class="tb-form-row no-border no-padding-bottom">
<mat-slide-toggle class="mat-slide" formControlName="useSystemSettings" (click)="$event.stopPropagation()">
{{ 'admin.mobile-app.use-system-settings' | translate }}
</mat-slide-toggle>
</div>
</div>
<div class="tb-form-panel no-border no-padding" *ngIf="!mobileAppQrCodeWidgetConfigForm.get('useSystemSettings').value">
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widget-config.appearance</div>
<div class="tb-form-hint tb-primary-fill">{{ 'widgets.mobile-app-qr-code.configuration-hint' | translate }}</div>
<div class="tb-form-row space-between no-padding-bottom column-xs">
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="badgeEnabled"
(click)="$event.stopPropagation()">
{{ 'admin.mobile-app.badges' | translate }}
</mat-slide-toggle>
<mat-form-field class="flex" appearance="outline" subscriptSizing="dynamic">
<mat-select formControlName="badgePosition" appearance="outline" subscriptSizing="dynamic">
<mat-option *ngFor="let badgePosition of badgePositionTranslationsMap | keyvalue" [value]="badgePosition.key">
{{ badgePosition.value | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="tb-form-row space-between no-padding-bottom column-xs">
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="qrCodeLabelEnabled" (click)="$event.stopPropagation()">
{{ 'admin.mobile-app.label' | translate }}
</mat-slide-toggle>
<mat-form-field class="flex" appearance="outline" subscriptSizing="dynamic">
<input matInput formControlName="qrCodeLabel" maxlength="50" placeholder="{{'admin.mobile-app.set' | translate}}">
<mat-icon matSuffix
matTooltipPosition="above"
matTooltipClass="tb-error-tooltip"
[matTooltip]="'admin.mobile-app.label-required' | translate"
*ngIf="mobileAppQrCodeWidgetConfigForm.get('qrCodeLabel').hasError('required')
&& mobileAppQrCodeWidgetConfigForm.get('qrCodeLabel').touched"
class="tb-error">
warning
</mat-icon>
</mat-form-field>
</div>
<div class="tb-form-row column-xs">
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showTitle">
{{ 'widgets.liquid-level-card.title' | translate }}
</mat-slide-toggle>
<div class="tb-flex row flex-start align-center">
<mat-form-field class="flex" appearance="outline" subscriptSizing="dynamic">
<input matInput formControlName="title" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
<tb-font-settings formControlName="titleFont"
[previewText]="mobileAppQrCodeWidgetConfigForm.get('title').value">
</tb-font-settings>
<tb-color-input asBoxInput colorClearButton formControlName="titleColor">
</tb-color-input>
</div>
</div>
<div class="tb-form-row column-xs">
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showTitleIcon">
{{ 'widgets.liquid-level-card.icon' | translate }}
</mat-slide-toggle>
<div class="tb-flex row flex-end align-center">
<mat-form-field appearance="outline" class="flex number" subscriptSizing="dynamic">
<input matInput type="number" min="0" formControlName="iconSize" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
<tb-css-unit-select fxFlex formControlName="iconSizeUnit"></tb-css-unit-select>
<tb-material-icon-select asBoxInput
iconClearButton
[color]="mobileAppQrCodeWidgetConfigForm.get('iconColor').value"
formControlName="titleIcon">
</tb-material-icon-select>
<tb-color-input asBoxInput colorClearButton formControlName="iconColor">
</tb-color-input>
</div>
</div>
</div>
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widget-config.card-appearance</div>
<div class="tb-form-row space-between">
<div>{{ 'widgets.background.background' | translate }}</div>
<tb-background-settings formControlName="background">
</tb-background-settings>
</div>
<div class="tb-form-row space-between">
<div translate>widget-config.show-card-buttons</div>
<mat-chip-listbox multiple formControlName="cardButtons">
<mat-chip-option value="fullscreen">{{ 'fullscreen.fullscreen' | translate }}</mat-chip-option>
</mat-chip-listbox>
</div>
<div class="tb-form-row space-between">
<div>{{ 'widget-config.card-border-radius' | translate }}</div>
<mat-form-field appearance="outline" subscriptSizing="dynamic">
<input matInput formControlName="borderRadius" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
</div>
</div>
<tb-widget-actions-panel
formControlName="actions">
</tb-widget-actions-panel>
</div>
</ng-container>

182
ui-ngx/src/app/modules/home/components/widget/config/basic/cards/mobile-app-qr-code-basic-config.component.ts

@ -0,0 +1,182 @@
///
/// 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 { ChangeDetectorRef, Component, Injector } from '@angular/core';
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models';
import { WidgetConfigComponentData } from '@home/models/widget-component.models';
import { WidgetConfig } from '@shared/models/widget.models';
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component';
import { isUndefined } from '@core/utils';
import { cssSizeToStrSize, resolveCssSize } from '@shared/models/widget-settings.models';
import {
mobileAppQrCodeWidgetDefaultSettings,
MobileAppQrCodeWidgetSettings
} from '@home/components/widget/lib/cards/mobile-app-qr-code-widget.models';
import { badgePositionTranslationsMap } from '@app/shared/models/mobile-app.models';
@Component({
selector: 'tb-mobile-app-qr-code-basic-config',
templateUrl: './mobile-app-qr-code-basic-config.component.html',
styleUrls: ['../basic-config.scss']
})
export class MobileAppQrCodeBasicConfigComponent extends BasicWidgetConfigComponent {
mobileAppQrCodeWidgetConfigForm: UntypedFormGroup;
badgePositionTranslationsMap = badgePositionTranslationsMap;
constructor(protected store: Store<AppState>,
protected widgetConfigComponent: WidgetConfigComponent,
private cd: ChangeDetectorRef,
private $injector: Injector,
private fb: UntypedFormBuilder) {
super(store, widgetConfigComponent);
}
protected configForm(): UntypedFormGroup {
return this.mobileAppQrCodeWidgetConfigForm;
}
protected setupConfig(widgetConfig: WidgetConfigComponentData) {
super.setupConfig(widgetConfig);
}
protected onConfigSet(configData: WidgetConfigComponentData) {
const settings: MobileAppQrCodeWidgetSettings = {...mobileAppQrCodeWidgetDefaultSettings, ...(configData.config.settings || {})};
const iconSize = resolveCssSize(configData.config.iconSize);
this.mobileAppQrCodeWidgetConfigForm = this.fb.group({
useSystemSettings: [settings.useSystemSettings],
badgeEnabled: [settings.qrCodeConfig.badgeEnabled],
badgePosition: [settings.qrCodeConfig.badgePosition],
qrCodeLabelEnabled: [settings.qrCodeConfig.qrCodeLabelEnabled],
qrCodeLabel: [settings.qrCodeConfig.qrCodeLabel],
showTitle: [configData.config.showTitle, []],
title: [configData.config.title, []],
titleFont: [configData.config.titleFont, []],
titleColor: [configData.config.titleColor, []],
showTitleIcon: [configData.config.showTitleIcon, []],
iconSize: [iconSize[0], [Validators.min(0)]],
iconSizeUnit: [iconSize[1], []],
titleIcon: [configData.config.titleIcon, []],
iconColor: [configData.config.iconColor, []],
background: [settings.background, []],
cardButtons: [this.getCardButtons(configData.config), []],
borderRadius: [configData.config.borderRadius, []],
actions: [configData.config.actions || {}, []]
});
}
protected prepareOutputConfig(config: any): WidgetConfigComponentData {
this.widgetConfig.config.showTitle = config.showTitle;
this.widgetConfig.config.title = config.title;
this.widgetConfig.config.titleFont = config.titleFont;
this.widgetConfig.config.titleColor = config.titleColor;
this.widgetConfig.config.showTitleIcon = config.showTitleIcon;
this.widgetConfig.config.iconSize = cssSizeToStrSize(config.iconSize, config.iconSizeUnit);
this.widgetConfig.config.titleIcon = config.titleIcon;
this.widgetConfig.config.iconColor = config.iconColor;
this.widgetConfig.config.settings = this.widgetConfig.config.settings || {};
this.widgetConfig.config.settings.useSystemSettings = config.useSystemSettings;
this.widgetConfig.config.settings.qrCodeConfig = {
badgeEnabled: config.badgeEnabled,
badgePosition: config.badgePosition,
qrCodeLabelEnabled: config.qrCodeLabelEnabled,
qrCodeLabel: config.qrCodeLabel
};
this.widgetConfig.config.settings.background = config.background;
this.setCardButtons(config.cardButtons, this.widgetConfig.config);
this.widgetConfig.config.borderRadius = config.borderRadius;
this.widgetConfig.config.actions = config.actions;
return this.widgetConfig;
}
protected validatorTriggers(): string[] {
return ['showTitleIcon', 'badgeEnabled', 'qrCodeLabelEnabled', 'showTitleIcon', 'showTitle'];
}
protected updateValidators(emitEvent: boolean, trigger?: string) {
const useSystemSettings = this.mobileAppQrCodeWidgetConfigForm.get('useSystemSettings').value;
if (!useSystemSettings) {
const badgeEnabled = this.mobileAppQrCodeWidgetConfigForm.get('badgeEnabled').value;
const qrCodeLabelEnabled = this.mobileAppQrCodeWidgetConfigForm.get('qrCodeLabelEnabled').value;
const showTitleIcon: boolean = this.mobileAppQrCodeWidgetConfigForm.get('showTitleIcon').value;
const showTitle: boolean = this.mobileAppQrCodeWidgetConfigForm.get('showTitle').value;
if (badgeEnabled) {
this.mobileAppQrCodeWidgetConfigForm.get('badgePosition').enable({emitEvent: false});
} else {
this.mobileAppQrCodeWidgetConfigForm.get('badgePosition').disable({emitEvent: false});
}
if (qrCodeLabelEnabled) {
this.mobileAppQrCodeWidgetConfigForm.get('qrCodeLabel').enable({emitEvent: false});
} else {
this.mobileAppQrCodeWidgetConfigForm.get('qrCodeLabel').disable({emitEvent: false});
}
if (showTitle) {
this.mobileAppQrCodeWidgetConfigForm.get('title').enable({emitEvent: false});
this.mobileAppQrCodeWidgetConfigForm.get('titleFont').enable({emitEvent: false});
this.mobileAppQrCodeWidgetConfigForm.get('titleColor').enable({emitEvent: false});
this.mobileAppQrCodeWidgetConfigForm.get('showTitleIcon').enable({emitEvent: false});
if (showTitleIcon) {
this.mobileAppQrCodeWidgetConfigForm.get('titleIcon').enable({emitEvent: false});
this.mobileAppQrCodeWidgetConfigForm.get('iconColor').enable({emitEvent: false});
this.mobileAppQrCodeWidgetConfigForm.get('iconSize').enable({emitEvent: false});
this.mobileAppQrCodeWidgetConfigForm.get('iconSizeUnit').enable({emitEvent: false});
} else {
this.mobileAppQrCodeWidgetConfigForm.get('titleIcon').disable({emitEvent: false});
this.mobileAppQrCodeWidgetConfigForm.get('iconColor').disable({emitEvent: false});
this.mobileAppQrCodeWidgetConfigForm.get('iconSize').disable({emitEvent: false});
this.mobileAppQrCodeWidgetConfigForm.get('iconSizeUnit').disable({emitEvent: false});
}
} else {
this.mobileAppQrCodeWidgetConfigForm.get('title').disable({emitEvent: false});
this.mobileAppQrCodeWidgetConfigForm.get('titleFont').disable({emitEvent: false});
this.mobileAppQrCodeWidgetConfigForm.get('titleColor').disable({emitEvent: false});
this.mobileAppQrCodeWidgetConfigForm.get('showTitleIcon').disable({emitEvent: false});
this.mobileAppQrCodeWidgetConfigForm.get('titleIcon').disable({emitEvent: false});
this.mobileAppQrCodeWidgetConfigForm.get('iconColor').disable({emitEvent: false});
this.mobileAppQrCodeWidgetConfigForm.get('iconSize').disable({emitEvent: false});
this.mobileAppQrCodeWidgetConfigForm.get('iconSizeUnit').disable({emitEvent: false});
}
}
}
private getCardButtons(config: WidgetConfig): string[] {
const buttons: string[] = [];
if (isUndefined(config.enableFullscreen) || config.enableFullscreen) {
buttons.push('fullscreen');
}
return buttons;
}
private setCardButtons(buttons: string[], config: WidgetConfig) {
config.enableFullscreen = buttons.includes('fullscreen');
}
}

39
ui-ngx/src/app/modules/home/components/widget/lib/cards/mobile-app-qr-code-widget.models.ts

@ -14,32 +14,41 @@
/// limitations under the License.
///
import {
AndroidConfig,
BadgePosition,
IosConfig,
QRCodeConfig
} from '@shared/models/mobile-app.models';
import { BadgePosition, QRCodeConfig } from '@shared/models/mobile-app.models';
import { BackgroundType } from '@shared/models/widget-settings.models';
import { WidgetConfig } from '@shared/models/widget.models';
export interface MobileAppQrCodeWidgetSettings {
export interface MobileAppQrCodeWidgetSettings extends WidgetConfig {
useSystemSettings: boolean;
androidConfig: Pick<AndroidConfig, 'enabled'>;
iosConfig: Pick<IosConfig, 'enabled'>;
qrCodeConfig: Omit<QRCodeConfig, 'showOnHomePage'>;
}
export const mobileAppQrCodeWidgetDefaultSettings: MobileAppQrCodeWidgetSettings = {
useSystemSettings: true,
androidConfig: {
enabled: true
},
iosConfig: {
enabled: true
},
qrCodeConfig: {
badgeEnabled: true,
badgePosition: BadgePosition.RIGHT,
qrCodeLabelEnabled: true,
qrCodeLabel: 'Scan to connect or download mobile app'
},
title: 'Download mobile app',
titleFont: {
family: 'Roboto',
size: 16,
sizeUnit: 'px',
style: 'normal',
weight: '500',
lineHeight: '1.5'
},
showTitleIcon: false,
iconSize: '40',
background: {
type: BackgroundType.color,
color: '#fff',
overlay: {
enabled: false,
color: 'rgba(255,255,255,0.72)',
blur: 3
}
}
}

17
ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.html

@ -15,14 +15,19 @@
limitations under the License.
-->
<div class="tb-flex column center align-center">
<div class="tb-flex row align-center shrink"
<div class="tb-mobile-app-qrcode-panel"
[class.preview]="previewMode"
[style]="backgroundStyle$ | async">
<div class="tb-mobile-app-qrcode-overlay" [style]="overlayStyle"></div>
<ng-container *ngTemplateOutlet="widgetTitlePanel"></ng-container>
<div class="tb-flex row center align-center"
[class.row]="mobileAppSettings?.qrCodeConfig.badgePosition === badgePosition.RIGHT"
[class.row-reverse]="mobileAppSettings?.qrCodeConfig.badgePosition === badgePosition.LEFT">
<canvas #canvas class="tb-qrcode" (click)="navigateByDeepLink($event)"></canvas>
<div class="tb-flex column" *ngIf="mobileAppSettings?.qrCodeConfig.badgeEnabled && showBadgeContainer">
<img *ngIf="mobileAppSettings.iosConfig.enabled" src="assets/android-ios-stores-badges/ios_store_en_black_badge.svg">
<img *ngIf="mobileAppSettings.androidConfig.enabled" src="assets/android-ios-stores-badges/android_store_en_black_badge.svg">
<div class="tb-qrcode" (click)="navigateByDeepLink($event)" [innerHTML]="qrCodeSVG | safe: 'html'" ></div>
<div class="tb-badges" *ngIf="(mobileAppSettings?.iosConfig.enabled || mobileAppSettings?.androidConfig.enabled) &&
mobileAppSettings?.qrCodeConfig.badgeEnabled && showBadgeContainer">
<img *ngIf="mobileAppSettings?.iosConfig.enabled" src="assets/android-ios-stores-badges/ios_store_en_black_badge.svg">
<img *ngIf="mobileAppSettings?.androidConfig.enabled" src="assets/android-ios-stores-badges/android_store_en_black_badge.svg">
</div>
</div>
<div *ngIf="mobileAppSettings?.qrCodeConfig.qrCodeLabelEnabled" class="tb-qrcode-label">

59
ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.scss

@ -17,23 +17,60 @@
@import '../../../../../../scss/constants';
:host {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
background-color: transparent;
justify-content: center;
}
.tb-qrcode-label {
font-size: 14px;
color: rgba(0, 0, 0, 0.54);
.tb-mobile-app-qrcode-panel {
width: 100%;
height: 100%;
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
gap: 8px;
padding: 16px;
&.preview {
max-width: 300px;
padding: 0;
}
> div:not(.tb-mobile-app-qrcode-overlay) {
z-index: 1;
}
.tb-mobile-app-qrcode-overlay {
position: absolute;
top: 12px;
left: 12px;
bottom: 12px;
right: 12px;
}
@media #{$mat-md-lg} {
font-size: 11px;
line-height: 16px;
div.tb-widget-title {
padding: 0;
}
.tb-qrcode-label {
align-self: center;
font-size: 14px;
color: rgba(0, 0, 0, 0.54);
}
}
.tb-qrcode {
border-radius: 6px;
.tb-qrcode {
display: flex;
flex: 0.3;
min-width: 100px;
padding: 3px;
border-radius: 6px;
background-color: #fff;
}
.tb-badges {
display: flex;
flex-direction: column;
flex: 0.4;
gap: 8px;
}
}

94
ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.ts

@ -14,7 +14,7 @@
/// limitations under the License.
///
import { ChangeDetectorRef, Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { ChangeDetectorRef, Component, ElementRef, Input, OnDestroy, OnInit, TemplateRef } from '@angular/core';
import { PageComponent } from '@shared/components/page.component';
import { AppState } from '@core/core.state';
import { Store } from '@ngrx/store';
@ -22,11 +22,13 @@ import { BadgePosition, MobileAppSettings } from '@shared/models/mobile-app.mode
import { MobileAppService } from '@core/http/mobile-app.service';
import { WidgetContext } from '@home/models/widget-component.models';
import { UtilsService } from '@core/services/utils.service';
import { Subject } from 'rxjs';
import { Observable, Subject } from 'rxjs';
import { MINUTE } from '@shared/models/time/time.models';
import { MobileAppQrCodeWidgetSettings } from '@home/components/widget/lib/cards/mobile-app-qr-code-widget.models';
import { isDefinedAndNotNull } from '@core/utils';
import { isDefinedAndNotNull, mergeDeep } from '@core/utils';
import { ResizeObserver } from '@juggle/resize-observer';
import { backgroundStyle, ComponentStyle, overlayStyle } from '@shared/models/widget-settings.models';
import { ImagePipe } from '@shared/pipe/image.pipe';
import { DomSanitizer } from '@angular/platform-browser';
@Component({
selector: 'tb-mobile-app-qrcode-widget',
@ -35,61 +37,76 @@ import { ResizeObserver } from '@juggle/resize-observer';
})
export class MobileAppQrcodeWidgetComponent extends PageComponent implements OnInit, OnDestroy {
private readonly destroy$ = new Subject<void>();
private widgetResize$: ResizeObserver;
private mobileAppSettingsValue: MobileAppSettings;
private deepLink: string;
private deepLinkTTL: number;
private deepLinkTTLTimeoutID: NodeJS.Timeout;
previewMode = false;
badgePosition = BadgePosition;
showBadgeContainer = true;
backgroundStyle$: Observable<ComponentStyle>;
overlayStyle: ComponentStyle = {};
qrCodeSVG = '';
@Input()
ctx: WidgetContext;
@Input()
set mobileAppSettings(settings: MobileAppSettings | MobileAppQrCodeWidgetSettings) {
widgetTitlePanel: TemplateRef<any>;
@Input()
set mobileAppSettings(settings: MobileAppSettings) {
if (settings) {
this.mobileAppSettingsValue = settings;
}
};
get mobileAppSettings(): MobileAppSettings | MobileAppQrCodeWidgetSettings {
get mobileAppSettings(): MobileAppSettings {
return this.mobileAppSettingsValue;
}
@ViewChild('canvas', {static: true}) canvasRef: ElementRef<HTMLCanvasElement>;
private readonly destroy$ = new Subject<void>();
private widgetResize$: ResizeObserver;
badgePosition = BadgePosition;
showBadgeContainer = true;
private mobileAppSettingsValue: MobileAppSettings | MobileAppQrCodeWidgetSettings;
private deepLink: string;
private deepLinkTTL: number;
private deepLinkTTLTimeoutID: NodeJS.Timeout;
constructor(protected store: Store<AppState>,
protected cd: ChangeDetectorRef,
private mobileAppService: MobileAppService,
private utilsService: UtilsService,
private elementRef: ElementRef) {
private elementRef: ElementRef,
private imagePipe: ImagePipe,
private sanitizer: DomSanitizer,) {
super(store);
}
ngOnInit(): void {
if (!this.mobileAppSettings) {
if (isDefinedAndNotNull(this.ctx.settings.useSystemSettings) && !this.ctx.settings.useSystemSettings) {
this.mobileAppSettings = this.ctx.settings;
} else {
this.mobileAppService.getMobileAppSettings().subscribe((settings => {
this.mobileAppSettings = settings;
this.cd.markForCheck();
}));
}
this.mobileAppService.getMobileAppSettings().subscribe((settings => {
this.mobileAppSettings = settings;
if (isDefinedAndNotNull(this.ctx.settings.useSystemSettings) && !this.ctx.settings.useSystemSettings) {
this.mobileAppSettings = mergeDeep(this.mobileAppSettings, this.ctx.settings);
}
this.widgetResize$ = new ResizeObserver(() => {
const showHideBadgeContainer = this.elementRef.nativeElement.offsetWidth > 250;
if (showHideBadgeContainer !== this.showBadgeContainer) {
this.showBadgeContainer = showHideBadgeContainer;
this.cd.markForCheck();
}
});
this.widgetResize$.observe(this.elementRef.nativeElement);
this.backgroundStyle$ = backgroundStyle(this.ctx.settings.background, this.imagePipe, this.sanitizer);
this.overlayStyle = overlayStyle(this.ctx.settings.background.overlay);
this.cd.markForCheck();
}));
} else {
this.previewMode = true;
}
this.initMobileAppQRCode();
this.widgetResize$ = new ResizeObserver(() => {
const showHideBadgeContainer = this.elementRef.nativeElement.offsetWidth > 250;
if (showHideBadgeContainer !== this.showBadgeContainer) {
this.showBadgeContainer = showHideBadgeContainer;
this.cd.markForCheck();
}
});
this.widgetResize$.observe(this.elementRef.nativeElement);
}
ngOnDestroy() {
@ -126,7 +143,10 @@ export class MobileAppQrcodeWidgetComponent extends PageComponent implements OnI
private updateQRCode(link: string) {
import('qrcode').then((QRCode) => {
QRCode.toCanvas(this.canvasRef.nativeElement, link, { width: 100 });
QRCode.toString(link, (err, string) => {
this.qrCodeSVG = string;
this.cd.markForCheck();
})
});
}

89
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/mobile-app-qr-code-widget-settings.component.html

@ -23,57 +23,50 @@
</mat-slide-toggle>
</div>
</div>
<div class="tb-form-panel" *ngIf="!mobileAppQRCodeWidgetSettingsForm.get('useSystemSettings').value">
<div class="tb-form-panel-title" translate>admin.mobile-app.applications</div>
<div class="tb-form-panel stroked no-padding no-gap">
<div class="tb-form-row no-border no-padding-bottom">
<mat-slide-toggle class="mat-slide" [formControl]="mobileAppQRCodeWidgetSettingsForm.get('androidConfig.enabled')" (click)="$event.stopPropagation()">
{{ 'admin.mobile-app.android' | translate }}
</mat-slide-toggle>
<div class="tb-form-panel no-border no-padding" *ngIf="!mobileAppQRCodeWidgetSettingsForm.get('useSystemSettings').value">
<div class="tb-form-panel" formGroupName="qrCodeConfig">
<div class="tb-form-panel-title" translate>widget-config.card-style</div>
<div class="tb-form-hint tb-primary-fill">{{ 'widgets.mobile-app-qr-code.configuration-hint' | translate }}</div>
<div class="tb-form-panel stroked no-padding no-gap">
<div class="tb-form-row space-between no-border no-padding-bottom column-xs">
<mat-slide-toggle class="mat-slide fixed-title-width-230" formControlName="badgeEnabled"
(click)="$event.stopPropagation()">
{{ 'admin.mobile-app.badges' | translate }}
</mat-slide-toggle>
<mat-form-field class="flex" appearance="outline" subscriptSizing="dynamic">
<mat-select formControlName="badgePosition" appearance="outline" subscriptSizing="dynamic">
<mat-option *ngFor="let badgePosition of badgePositionTranslationsMap | keyvalue" [value]="badgePosition.key">
{{ badgePosition.value | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
</div>
<div class="tb-form-panel stroked no-padding no-gap" formGroupName="iosConfig">
<div class="tb-form-row no-border no-padding-bottom">
<mat-slide-toggle class="mat-slide" [formControl]="mobileAppQRCodeWidgetSettingsForm.get('iosConfig.enabled')" (click)="$event.stopPropagation()">
{{ 'admin.mobile-app.ios' | translate }}
</mat-slide-toggle>
</div>
</div>
</div>
<div class="tb-form-panel" formGroupName="qrCodeConfig" *ngIf="!mobileAppQRCodeWidgetSettingsForm.get('useSystemSettings').value">
<div class="tb-form-panel-title" translate>admin.mobile-app.appearance</div>
<div class="tb-form-panel stroked no-padding no-gap">
<div class="tb-form-row space-between no-border no-padding-bottom column-xs">
<mat-slide-toggle class="mat-slide fixed-title-width-230" formControlName="badgeEnabled"
(click)="$event.stopPropagation()">
{{ 'admin.mobile-app.badges' | translate }}
</mat-slide-toggle>
<mat-form-field class="flex" appearance="outline" subscriptSizing="dynamic">
<mat-select formControlName="badgePosition" appearance="outline" subscriptSizing="dynamic">
<mat-option *ngFor="let badgePosition of badgePositionTranslationsMap | keyvalue" [value]="badgePosition.key">
{{ badgePosition.value | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<div class="tb-form-panel stroked no-padding no-gap">
<div class="tb-form-row space-between no-border no-padding-bottom column-xs">
<mat-slide-toggle class="mat-slide fixed-title-width-230" formControlName="qrCodeLabelEnabled" (click)="$event.stopPropagation()">
{{ 'admin.mobile-app.label' | translate }}
</mat-slide-toggle>
<mat-form-field class="flex" appearance="outline" subscriptSizing="dynamic">
<input matInput formControlName="qrCodeLabel" maxlength="50" placeholder="{{'admin.mobile-app.set' | translate}}">
<mat-icon matSuffix
matTooltipPosition="above"
matTooltipClass="tb-error-tooltip"
[matTooltip]="'admin.mobile-app.label-required' | translate"
*ngIf="mobileAppQRCodeWidgetSettingsForm.get('qrCodeConfig.qrCodeLabel').hasError('required')
&& mobileAppQRCodeWidgetSettingsForm.get('qrCodeConfig.qrCodeLabel').touched"
class="tb-error">
warning
</mat-icon>
</mat-form-field>
</div>
</div>
</div>
<div class="tb-form-panel stroked no-padding no-gap">
<div class="tb-form-row space-between no-border no-padding-bottom column-xs">
<mat-slide-toggle class="mat-slide fixed-title-width-230" formControlName="qrCodeLabelEnabled" (click)="$event.stopPropagation()">
{{ 'admin.mobile-app.label' | translate }}
</mat-slide-toggle>
<mat-form-field class="flex" appearance="outline" subscriptSizing="dynamic">
<input matInput formControlName="qrCodeLabel" maxlength="50" placeholder="{{'admin.mobile-app.set' | translate}}">
<mat-icon matSuffix
matTooltipPosition="above"
matTooltipClass="tb-error-tooltip"
[matTooltip]="'admin.mobile-app.label-required' | translate"
*ngIf="mobileAppQRCodeWidgetSettingsForm.get('qrCodeConfig.qrCodeLabel').hasError('required')
&& mobileAppQRCodeWidgetSettingsForm.get('qrCodeConfig.qrCodeLabel').touched"
class="tb-error">
warning
</mat-icon>
</mat-form-field>
<div class="tb-form-panel no-border no-padding">
<div class="tb-form-row space-between">
<div>{{ 'widgets.background.background' | translate }}</div>
<tb-background-settings formControlName="background">
</tb-background-settings>
</div>
</div>
</div>

41
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/mobile-app-qr-code-widget-settings.component.ts

@ -49,50 +49,35 @@ export class MobileAppQrCodeWidgetSettingsComponent extends WidgetSettingsCompon
protected onSettingsSet(settings: WidgetSettings) {
this.mobileAppQRCodeWidgetSettingsForm = this.fb.group({
useSystemSettings: [settings.useSystemSettings],
androidConfig: this.fb.group({
enabled: [settings.androidConfig.enabled],
}),
iosConfig: this.fb.group({
enabled: [settings.iosConfig.enabled],
}),
qrCodeConfig: this.fb.group({
badgeEnabled: [settings.qrCodeConfig.badgeEnabled],
badgePosition: [{value: settings.qrCodeConfig.badgePosition, disabled: true}],
badgePosition: [settings.qrCodeConfig.badgePosition],
qrCodeLabelEnabled: [settings.qrCodeConfig.qrCodeLabelEnabled],
qrCodeLabel: [settings.qrCodeConfig.qrCodeLabel, [Validators.required]]
})
qrCodeLabel: [settings.qrCodeConfig.qrCodeLabel]
}),
background: [settings.background]
});
}
protected validatorTriggers(): string[] {
return ['useSystemSettings', 'androidConfig.enabled', 'iosConfig.enabled', 'qrCodeConfig.badgeEnabled', 'qrCodeConfig.qrCodeLabelEnabled'];
return ['useSystemSettings', 'qrCodeConfig.badgeEnabled', 'qrCodeConfig.qrCodeLabelEnabled'];
}
protected updateValidators(emitEvent: boolean) {
const useSystemSettings = this.mobileAppQRCodeWidgetSettingsForm.get('useSystemSettings').value;
const androidEnabled = this.mobileAppQRCodeWidgetSettingsForm.get('androidConfig.enabled').value;
const iosEnabled = this.mobileAppQRCodeWidgetSettingsForm.get('iosConfig.enabled').value;
const badgeEnabled = this.mobileAppQRCodeWidgetSettingsForm.get('qrCodeConfig.badgeEnabled').value;
const qrCodeLabelEnabled = this.mobileAppQRCodeWidgetSettingsForm.get('qrCodeConfig.qrCodeLabelEnabled').value;
if (!androidEnabled && !iosEnabled) {
this.mobileAppQRCodeWidgetSettingsForm.get('qrCodeConfig.badgeEnabled').disable({emitEvent: false});
this.mobileAppQRCodeWidgetSettingsForm.get('qrCodeConfig.badgePosition').disable({emitEvent: false});
}
if (androidEnabled || iosEnabled) {
this.mobileAppQRCodeWidgetSettingsForm.get('qrCodeConfig.badgeEnabled').enable({emitEvent: false});
if (!useSystemSettings) {
const badgeEnabled = this.mobileAppQRCodeWidgetSettingsForm.get('qrCodeConfig.badgeEnabled').value;
const qrCodeLabelEnabled = this.mobileAppQRCodeWidgetSettingsForm.get('qrCodeConfig.qrCodeLabelEnabled').value;
if (badgeEnabled) {
this.mobileAppQRCodeWidgetSettingsForm.get('qrCodeConfig.badgePosition').enable({emitEvent: false});
} else {
this.mobileAppQRCodeWidgetSettingsForm.get('qrCodeConfig.badgePosition').disable({emitEvent: false});
}
}
if (qrCodeLabelEnabled && !useSystemSettings) {
this.mobileAppQRCodeWidgetSettingsForm.get('qrCodeConfig.qrCodeLabel').enable({emitEvent: false});
} else {
this.mobileAppQRCodeWidgetSettingsForm.get('qrCodeConfig.qrCodeLabel').disable({emitEvent: false});
if (qrCodeLabelEnabled) {
this.mobileAppQRCodeWidgetSettingsForm.get('qrCodeConfig.qrCodeLabel').enable({emitEvent: false});
} else {
this.mobileAppQRCodeWidgetSettingsForm.get('qrCodeConfig.qrCodeLabel').disable({emitEvent: false});
}
}
}

26
ui-ngx/src/assets/dashboard/sys_admin_home_page.json

@ -2642,16 +2642,34 @@
"showTitle": true,
"backgroundColor": "rgb(255, 255, 255)",
"color": "rgba(0, 0, 0, 0.87)",
"padding": "8px",
"settings": {},
"padding": "16px",
"settings": {
"useSystemSettings": true,
"qrCodeConfig": {
"badgeEnabled": true,
"badgePosition": "RIGHT",
"qrCodeLabelEnabled": true,
"qrCodeLabel": "Scan to connect or download mobile app"
},
"background": {
"type": "color",
"color": "#fff",
"overlay": {
"enabled": false,
"color": "rgba(255,255,255,0.72)",
"blur": 3
}
}
},
"title": "{i18n:admin.mobile-app.connect-mobile-app}",
"dropShadow": false,
"enableFullscreen": false,
"widgetStyle": {},
"widgetCss": ".tb-widget-container > .tb-widget > .tb-widget-header > .tb-widget-title {\n padding: 0;\n}\n\n.tb-widget-container > .tb-widget > .tb-widget-header > .tb-widget-title > .title-row > .title {\n padding-bottom: 12px;\n font-weight: 600;\n font-size: 20px;\n line-height: 24px;\n letter-spacing: 0.1px;\n color: rgba(0, 0, 0, 0.76);\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-widget-container > .tb-widget > .tb-widget-header > .tb-widget-title > .title-row > .title {\n padding-bottom: 0;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n }\n}\n",
"widgetCss": " .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding: 0;\n}\n\n .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding-bottom: 8px;\n font-weight: 600;\n font-size: 20px;\n line-height: 24px;\n letter-spacing: 0.1px;\n color: rgba(0, 0, 0, 0.76);\n}\n\n.tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel {\n padding: 0;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding-bottom: 0;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode-label {\n display: none;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1190px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel {\n gap: 0;\n }\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n min-width: 150px;\n }\n}\n\n@media screen and (max-width: 960px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n max-width: 180px;\n }\n \n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-badges {\n max-width: 240px;\n }\n}",
"showTitleIcon": false,
"titleTooltip": "",
"titleStyle": null
"titleStyle": null,
"borderRadius": "12px"
},
"row": 0,
"col": 0,

26
ui-ngx/src/assets/dashboard/tenant_admin_home_page.json

@ -1185,16 +1185,34 @@
"showTitle": true,
"backgroundColor": "rgb(255, 255, 255)",
"color": "rgba(0, 0, 0, 0.87)",
"padding": "8px",
"settings": {},
"padding": "16px",
"settings": {
"useSystemSettings": true,
"qrCodeConfig": {
"badgeEnabled": true,
"badgePosition": "RIGHT",
"qrCodeLabelEnabled": true,
"qrCodeLabel": "Scan to connect or download mobile app"
},
"background": {
"type": "color",
"color": "#fff",
"overlay": {
"enabled": false,
"color": "rgba(255,255,255,0.72)",
"blur": 3
}
}
},
"title": "{i18n:admin.mobile-app.connect-mobile-app}",
"dropShadow": false,
"enableFullscreen": false,
"widgetStyle": {},
"widgetCss": ".tb-widget-container > .tb-widget > .tb-widget-header > .tb-widget-title {\n padding: 0;\n}\n\n.tb-widget-container > .tb-widget > .tb-widget-header > .tb-widget-title > .title-row > .title {\n padding-bottom: 12px;\n font-weight: 600;\n font-size: 20px;\n line-height: 24px;\n letter-spacing: 0.1px;\n color: rgba(0, 0, 0, 0.76);\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-widget-container > .tb-widget > .tb-widget-header > .tb-widget-title > .title-row > .title {\n padding-bottom: 0;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n }\n}\n",
"widgetCss": " .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding: 0;\n}\n\n .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding-bottom: 8px;\n font-weight: 600;\n font-size: 20px;\n line-height: 24px;\n letter-spacing: 0.1px;\n color: rgba(0, 0, 0, 0.76);\n}\n\n.tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel {\n padding: 0;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding-bottom: 0;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode-label {\n display: none;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1190px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel {\n gap: 0;\n }\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n min-width: 150px;\n }\n}\n\n@media screen and (max-width: 960px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n max-width: 180px;\n }\n \n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-badges {\n max-width: 240px;\n }\n}",
"showTitleIcon": false,
"titleTooltip": "",
"titleStyle": null
"titleStyle": null,
"borderRadius": "12px"
},
"row": 0,
"col": 0,

3
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -5557,6 +5557,9 @@
"invalid-widget-file-error": "Unable to import widget: Invalid widget data structure."
},
"widgets": {
"mobile-app-qr-code": {
"configuration-hint": "The configuration depends on the Mobile app QR code widget in platform main settings"
},
"action-button": {
"behavior": "Behavior",
"on-click": "On click",

Loading…
Cancel
Save