Browse Source

Merge pull request #10838 from rusikv/enhancement/mobile-app-qr-code-improvements

[QR-code widget] - Styles improvements, added label limit
pull/10910/head
Igor Kulikov 2 years ago
committed by GitHub
parent
commit
99cb27f8f9
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 10
      application/src/main/data/json/system/widget_types/mobile_app_qr_code.json
  2. 4
      application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java
  3. 24
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/BadgeStyle.java
  4. 1
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/QRCodeConfig.java
  5. 2
      dao/src/main/java/org/thingsboard/server/dao/mobile/BaseMobileAppSettingsService.java
  6. 9
      ui-ngx/src/app/core/auth/auth.actions.ts
  7. 3
      ui-ngx/src/app/core/auth/auth.reducer.ts
  8. 10
      ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts
  9. 118
      ui-ngx/src/app/modules/home/components/widget/config/basic/cards/mobile-app-qr-code-basic-config.component.html
  10. 182
      ui-ngx/src/app/modules/home/components/widget/config/basic/cards/mobile-app-qr-code-basic-config.component.ts
  11. 41
      ui-ngx/src/app/modules/home/components/widget/lib/cards/mobile-app-qr-code-widget.models.ts
  12. 35
      ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.html
  13. 61
      ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.scss
  14. 107
      ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.ts
  15. 98
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/mobile-app-qr-code-widget-settings.component.html
  16. 48
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/mobile-app-qr-code-widget-settings.component.ts
  17. 21
      ui-ngx/src/app/modules/home/pages/admin/mobile-app-settings.component.html
  18. 60
      ui-ngx/src/app/modules/home/pages/admin/mobile-app-settings.component.ts
  19. 5
      ui-ngx/src/app/modules/home/pages/home-links/home-links-routing.module.ts
  20. 22
      ui-ngx/src/app/shared/models/mobile-app.models.ts
  21. 53
      ui-ngx/src/assets/android-ios-stores-badges/android_store_en_black_badge.svg
  22. 63
      ui-ngx/src/assets/android-ios-stores-badges/ios_store_en_black_badge.svg
  23. 46
      ui-ngx/src/assets/android-ios-stores-badges/ios_store_en_white_badge.svg
  24. 28
      ui-ngx/src/assets/dashboard/sys_admin_home_page.json
  25. 30
      ui-ngx/src/assets/dashboard/tenant_admin_home_page.json
  26. 6
      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",

4
application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java

@ -97,7 +97,7 @@ public class MobileApplicationController extends BaseController {
public ResponseEntity<JsonNode> getAssetLinks() {
MobileAppSettings mobileAppSettings = mobileAppSettingsService.getMobileAppSettings(TenantId.SYS_TENANT_ID);
AndroidConfig androidConfig = mobileAppSettings.getAndroidConfig();
if (androidConfig != null && androidConfig.isEnabled() && !androidConfig.getAppPackage().isBlank() && !androidConfig.getSha256CertFingerprints().isBlank()) {
if (androidConfig != null && androidConfig.isEnabled() && androidConfig.getAppPackage() != null && androidConfig.getSha256CertFingerprints() != null) {
return ResponseEntity.ok(JacksonUtil.toJsonNode(String.format(ASSET_LINKS_PATTERN, androidConfig.getAppPackage(), androidConfig.getSha256CertFingerprints())));
} else {
return ResponseEntity.notFound().build();
@ -109,7 +109,7 @@ public class MobileApplicationController extends BaseController {
public ResponseEntity<JsonNode> getAppleAppSiteAssociation() {
MobileAppSettings mobileAppSettings = mobileAppSettingsService.getMobileAppSettings(TenantId.SYS_TENANT_ID);
IosConfig iosConfig = mobileAppSettings.getIosConfig();
if (iosConfig != null && iosConfig.isEnabled() && !iosConfig.getAppId().isBlank()) {
if (iosConfig != null && iosConfig.isEnabled() && iosConfig.getAppId() != null) {
return ResponseEntity.ok(JacksonUtil.toJsonNode(String.format(APPLE_APP_SITE_ASSOCIATION_PATTERN, iosConfig.getAppId())));
} else {
return ResponseEntity.notFound().build();

24
common/data/src/main/java/org/thingsboard/server/common/data/mobile/BadgeStyle.java

@ -1,24 +0,0 @@
/**
* 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.
*/
package org.thingsboard.server.common.data.mobile;
public enum BadgeStyle {
ORIGINAL,
WHITE;
}

1
common/data/src/main/java/org/thingsboard/server/common/data/mobile/QRCodeConfig.java

@ -33,7 +33,6 @@ public class QRCodeConfig {
private boolean badgeEnabled;
private boolean qrCodeLabelEnabled;
private BadgePosition badgePosition;
private BadgeStyle badgeStyle;
@NoXss
private String qrCodeLabel;

2
dao/src/main/java/org/thingsboard/server/dao/mobile/BaseMobileAppSettingsService.java

@ -22,7 +22,6 @@ import org.springframework.transaction.event.TransactionalEventListener;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.mobile.AndroidConfig;
import org.thingsboard.server.common.data.mobile.BadgePosition;
import org.thingsboard.server.common.data.mobile.BadgeStyle;
import org.thingsboard.server.common.data.mobile.IosConfig;
import org.thingsboard.server.common.data.mobile.MobileAppSettings;
import org.thingsboard.server.common.data.mobile.QRCodeConfig;
@ -98,7 +97,6 @@ public class BaseMobileAppSettingsService extends AbstractCachedEntityService<Te
.qrCodeLabel(DEFAULT_QR_CODE_LABEL)
.badgeEnabled(true)
.badgePosition(BadgePosition.RIGHT)
.badgeStyle(BadgeStyle.ORIGINAL)
.badgeEnabled(true)
.build();

9
ui-ngx/src/app/core/auth/auth.actions.ts

@ -27,6 +27,7 @@ export enum AuthActionTypes {
UPDATE_AUTH_USER = '[Auth] Update Auth User',
UPDATE_LAST_PUBLIC_DASHBOARD_ID = '[Auth] Update Last Public Dashboard Id',
UPDATE_HAS_REPOSITORY = '[Auth] Change Has Repository',
UPDATE_MOBILE_QR_ENABLED = '[Auth] Update Mobile QR Enabled',
UPDATE_OPENED_MENU_SECTION = '[Preferences] Update Opened Menu Section',
PUT_USER_SETTINGS = '[Preferences] Put user settings',
DELETE_USER_SETTINGS = '[Preferences] Delete user settings',
@ -72,6 +73,12 @@ export class ActionAuthUpdateHasRepository implements Action {
constructor(readonly payload: { hasRepository: boolean }) {}
}
export class ActionUpdateMobileQrCodeEnabled implements Action {
readonly type = AuthActionTypes.UPDATE_MOBILE_QR_ENABLED;
constructor(readonly payload: { mobileQrEnabled: boolean }) {}
}
export class ActionPreferencesUpdateOpenedMenuSection implements Action {
readonly type = AuthActionTypes.UPDATE_OPENED_MENU_SECTION;
@ -93,4 +100,4 @@ export class ActionPreferencesDeleteUserSettings implements Action {
export type AuthActions = ActionAuthAuthenticated | ActionAuthUnauthenticated |
ActionAuthLoadUser | ActionAuthUpdateUserDetails | ActionAuthUpdateLastPublicDashboardId | ActionAuthUpdateHasRepository |
ActionPreferencesUpdateOpenedMenuSection | ActionPreferencesPutUserSettings | ActionPreferencesDeleteUserSettings |
ActionAuthUpdateAuthUser;
ActionAuthUpdateAuthUser | ActionUpdateMobileQrCodeEnabled;

3
ui-ngx/src/app/core/auth/auth.reducer.ts

@ -70,6 +70,9 @@ export const authReducer = (
case AuthActionTypes.UPDATE_HAS_REPOSITORY:
return { ...state, ...action.payload};
case AuthActionTypes.UPDATE_MOBILE_QR_ENABLED:
return { ...state, ...action.payload};
case AuthActionTypes.UPDATE_OPENED_MENU_SECTION:
const openedMenuSections = new Set(state.userSettings.openedMenuSections);
if (action.payload.opened) {

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

@ -132,6 +132,7 @@ import {
import {
DigitalSimpleGaugeBasicConfigComponent
} from '@home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component';
import { MobileAppQrCodeBasicConfigComponent } from '@home/components/widget/config/basic/cards/mobile-app-qr-code-basic-config.component';
@NgModule({
declarations: [
@ -175,7 +176,8 @@ import {
BarChartBasicConfigComponent,
PolarAreaChartBasicConfigComponent,
RadarChartBasicConfigComponent,
DigitalSimpleGaugeBasicConfigComponent
DigitalSimpleGaugeBasicConfigComponent,
MobileAppQrCodeBasicConfigComponent
],
imports: [
CommonModule,
@ -221,7 +223,8 @@ import {
BarChartBasicConfigComponent,
PolarAreaChartBasicConfigComponent,
RadarChartBasicConfigComponent,
DigitalSimpleGaugeBasicConfigComponent
DigitalSimpleGaugeBasicConfigComponent,
MobileAppQrCodeBasicConfigComponent
]
})
export class BasicWidgetConfigModule {
@ -261,5 +264,6 @@ export const basicWidgetConfigComponentsMap: {[key: string]: Type<IBasicWidgetCo
'tb-bar-chart-basic-config': BarChartBasicConfigComponent,
'tb-polar-area-chart-basic-config': PolarAreaChartBasicConfigComponent,
'tb-radar-chart-basic-config': RadarChartBasicConfigComponent,
'tb-digital-simple-gauge-basic-config': DigitalSimpleGaugeBasicConfigComponent
'tb-digital-simple-gauge-basic-config': DigitalSimpleGaugeBasicConfigComponent,
'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');
}
}

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

@ -14,34 +14,41 @@
/// limitations under the License.
///
import {
AndroidConfig,
BadgePosition,
BadgeStyle,
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,
badgeStyle: BadgeStyle.ORIGINAL,
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
}
}
}

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

@ -15,23 +15,24 @@
limitations under the License.
-->
<div class="tb-flex column center align-center">
<div class="tb-flex row align-center shrink"
[class.row]="mobileAppSettings?.qrCodeConfig.badgePosition === badgePosition.RIGHT"
[class.row-reverse]="mobileAppSettings?.qrCodeConfig.badgePosition === badgePosition.LEFT">
<canvas #canvas class="tb-qrcode"></canvas>
<div class="tb-flex column" *ngIf="mobileAppSettings?.qrCodeConfig.badgeEnabled && showBadgeContainer">
<ng-container *ngIf="mobileAppSettings.qrCodeConfig.badgeStyle === badgeStyle.ORIGINAL;else white">
<img *ngIf="mobileAppSettings.iosConfig.enabled" [src]="badgeStyleURLMap.get(badgeStyle.ORIGINAL).iOS">
<img *ngIf="mobileAppSettings.androidConfig.enabled" [src]="badgeStyleURLMap.get(badgeStyle.ORIGINAL).android">
</ng-container>
<ng-template #white>
<img *ngIf="mobileAppSettings.iosConfig.enabled" [src]="badgeStyleURLMap.get(badgeStyle.WHITE).iOS">
<img *ngIf="mobileAppSettings.androidConfig.enabled" [src]="badgeStyleURLMap.get(badgeStyle.WHITE).android">
</ng-template>
<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 column center">
<div class="tb-flex row center align-center shrink"
[class.row]="mobileAppSettings?.qrCodeConfig.badgePosition === badgePosition.RIGHT"
[class.row-reverse]="mobileAppSettings?.qrCodeConfig.badgePosition === badgePosition.LEFT">
<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 class="tb-qrcode-label" *ngIf="mobileAppSettings?.qrCodeConfig.qrCodeLabelEnabled">
{{ mobileAppSettings.qrCodeConfig.qrCodeLabel }}
</div>
</div>
<div *ngIf="mobileAppSettings?.qrCodeConfig.qrCodeLabelEnabled" class="tb-qrcode-label">
{{ mobileAppSettings.qrCodeConfig.qrCodeLabel }}
</div>
</div>

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

@ -17,24 +17,61 @@
@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;
text-align: center;
font-size: 14px;
color: rgba(0, 0, 0, 0.54);
}
}
.tb-qrcode {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.08);
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;
}
}

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

@ -14,19 +14,21 @@
/// 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';
import { BadgePosition, BadgeStyle, badgeStyleURLMap, MobileAppSettings } from '@shared/models/mobile-app.models';
import { BadgePosition, MobileAppSettings } from '@shared/models/mobile-app.models';
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,62 +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;
badgeStyle = BadgeStyle;
badgePosition = BadgePosition;
badgeStyleURLMap = badgeStyleURLMap;
showBadgeContainer = true;
private mobileAppSettingsValue: MobileAppSettings | MobileAppQrCodeWidgetSettings;
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() {
@ -103,12 +119,22 @@ export class MobileAppQrcodeWidgetComponent extends PageComponent implements OnI
clearTimeout(this.deepLinkTTLTimeoutID);
}
navigateByDeepLink($event) {
if ($event) {
$event.stopPropagation();
}
if (this.ctx.isMobile) {
window.open(this.deepLink, '_blank');
}
}
private initMobileAppQRCode() {
if (this.deepLinkTTLTimeoutID) {
clearTimeout(this.deepLinkTTLTimeoutID);
this.deepLinkTTLTimeoutID = null;
}
this.mobileAppService.getMobileAppDeepLink().subscribe(link => {
this.deepLink = link;
this.deepLinkTTL = Number(this.utilsService.getQueryParam('ttl', link)) * MINUTE;
this.updateQRCode(link);
this.deepLinkTTLTimeoutID = setTimeout(() => this.initMobileAppQRCode(), this.deepLinkTTL);
@ -117,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();
})
});
}

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

@ -16,71 +16,57 @@
-->
<section class="tb-widget-settings tb-form-panel no-border no-padding" [formGroup]="mobileAppQRCodeWidgetSettingsForm">
<div class="tb-form-panel no-padding">
<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" *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="badgeStyle" appearance="outline" subscriptSizing="dynamic">
<mat-option *ngFor="let badgeStyle of badgeStyleTranslationsMap | keyvalue" [value]="badgeStyle.key">
{{ badgeStyle.value | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<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>

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

@ -19,7 +19,7 @@ import { UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms
import { WidgetSettings, WidgetSettingsComponent } from "@shared/models/widget.models";
import { AppState } from '@core/core.state';
import { Store } from "@ngrx/store";
import { badgePositionTranslationsMap, badgeStyleTranslationsMap } from '@shared/models/mobile-app.models';
import { badgePositionTranslationsMap } from '@shared/models/mobile-app.models';
import { mobileAppQrCodeWidgetDefaultSettings } from '@home/components/widget/lib/cards/mobile-app-qr-code-widget.models';
@Component({
@ -32,7 +32,6 @@ export class MobileAppQrCodeWidgetSettingsComponent extends WidgetSettingsCompon
mobileAppQRCodeWidgetSettingsForm: UntypedFormGroup;
badgePositionTranslationsMap = badgePositionTranslationsMap;
badgeStyleTranslationsMap = badgeStyleTranslationsMap;
constructor(protected store: Store<AppState>,
private fb: UntypedFormBuilder) {
@ -50,54 +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],
badgeStyle: [{value: settings.qrCodeConfig.badgeStyle, disabled: true}],
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.badgeStyle').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.badgeStyle').enable({emitEvent: false});
this.mobileAppQRCodeWidgetSettingsForm.get('qrCodeConfig.badgePosition').enable({emitEvent: false});
} else {
this.mobileAppQRCodeWidgetSettingsForm.get('qrCodeConfig.badgeStyle').disable({emitEvent: false});
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});
}
}
}

21
ui-ngx/src/app/modules/home/pages/admin/mobile-app-settings.component.html

@ -27,7 +27,7 @@
<mat-card-content style="padding-top: 16px">
<div class="tb-form-panel no-border no-padding" [formGroup]="mobileAppSettingsForm">
<div class="tb-form-panel">
<div fxLayout="row" fxLayoutAlign="space-between center">
<div class="tb-form-row column-xs no-border no-padding space-between">
<div class="tb-form-panel-title" translate>admin.mobile-app.applications</div>
<tb-toggle-select formControlName="useDefaultApp">
<tb-toggle-option [value]="true">{{ 'admin.mobile-app.default' | translate }}</tb-toggle-option>
@ -104,7 +104,7 @@
</div>
</div>
<div class="tb-form-panel" formGroupName="qrCodeConfig">
<div class="tb-flex row space-between align-center">
<div class="tb-form-row column-xs no-border no-padding space-between">
<div class="tb-form-panel-title" translate>admin.mobile-app.appearance-on-home-page</div>
<tb-toggle-select formControlName="showOnHomePage">
<tb-toggle-option [value]="true">{{ 'admin.mobile-app.enabled' | translate }}</tb-toggle-option>
@ -117,13 +117,6 @@
(click)="$event.stopPropagation()">
{{ 'admin.mobile-app.badges' | translate }}
</mat-slide-toggle>
<mat-form-field class="flex" appearance="outline" subscriptSizing="dynamic">
<mat-select formControlName="badgeStyle" appearance="outline" subscriptSizing="dynamic">
<mat-option *ngFor="let badgeStyle of badgeStyleTranslationsMap | keyvalue" [value]="badgeStyle.key">
{{ badgeStyle.value | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<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">
@ -139,13 +132,15 @@
{{ '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}}">
<input matInput formControlName="qrCodeLabel" placeholder="{{'admin.mobile-app.set' | translate}}">
<mat-icon matSuffix
matTooltipPosition="above"
matTooltipClass="tb-error-tooltip"
[matTooltip]="'admin.mobile-app.label-required' | translate"
*ngIf="mobileAppSettingsForm.get('qrCodeConfig.qrCodeLabel').hasError('required')
&& mobileAppSettingsForm.get('qrCodeConfig.qrCodeLabel').touched"
[matTooltip]="(mobileAppSettingsForm.get('qrCodeConfig.qrCodeLabel').hasError('required') ?
'admin.mobile-app.label-required' : 'admin.mobile-app.label-max-length') | translate"
*ngIf="mobileAppSettingsForm.get('qrCodeConfig.qrCodeLabel').touched &&
mobileAppSettingsForm.get('qrCodeConfig.qrCodeLabel').hasError('required') ||
mobileAppSettingsForm.get('qrCodeConfig.qrCodeLabel').hasError('maxlength')"
class="tb-error">
warning
</mat-icon>

60
ui-ngx/src/app/modules/home/pages/admin/mobile-app-settings.component.ts

@ -25,10 +25,9 @@ import { MobileAppService } from '@core/http/mobile-app.service';
import {
BadgePosition,
badgePositionTranslationsMap,
BadgeStyle,
badgeStyleTranslationsMap,
MobileAppSettings
} from '@shared/models/mobile-app.models';
import { ActionUpdateMobileQrCodeEnabled } from '@core/auth/auth.actions';
@Component({
selector: 'tb-mobile-app-settings',
@ -44,7 +43,6 @@ export class MobileAppSettingsComponent extends PageComponent implements HasConf
private readonly destroy$ = new Subject<void>();
badgePositionTranslationsMap = badgePositionTranslationsMap;
badgeStyleTranslationsMap = badgeStyleTranslationsMap;
constructor(protected store: Store<AppState>,
private mobileAppService: MobileAppService,
@ -80,23 +78,37 @@ export class MobileAppSettingsComponent extends PageComponent implements HasConf
).subscribe(value => {
this.iosEnableChanged(value);
});
this.mobileAppSettingsForm.get('qrCodeConfig.showOnHomePage').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe(value => {
if (value) {
this.mobileAppSettingsForm.get('qrCodeConfig').enable({emitEvent: false});
} else {
this.mobileAppSettingsForm.get('qrCodeConfig').disable({emitEvent: false});
this.mobileAppSettingsForm.get('qrCodeConfig.showOnHomePage').enable({emitEvent: false});
}
this.mobileAppSettingsForm.get('qrCodeConfig.badgeEnabled').updateValueAndValidity({onlySelf: true});
this.mobileAppSettingsForm.get('qrCodeConfig.qrCodeLabelEnabled').updateValueAndValidity({onlySelf: true});
});
this.mobileAppSettingsForm.get('qrCodeConfig.badgeEnabled').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe(value => {
if (value) {
if (this.mobileAppSettingsForm.get('androidConfig.enabled').value || this.mobileAppSettingsForm.get('iosConfig.enabled').value) {
this.mobileAppSettingsForm.get('qrCodeConfig.badgeStyle').enable({emitEvent: false});
this.mobileAppSettingsForm.get('qrCodeConfig.badgeEnabled').enable({emitEvent: false});
this.mobileAppSettingsForm.get('qrCodeConfig.badgePosition').enable({emitEvent: false});
} else {
this.mobileAppSettingsForm.get('qrCodeConfig.badgeEnabled').disable({emitEvent: false});
this.mobileAppSettingsForm.get('qrCodeConfig.badgePosition').disable({emitEvent: false});
}
} else {
this.mobileAppSettingsForm.get('qrCodeConfig.badgeStyle').disable({emitEvent: false});
this.mobileAppSettingsForm.get('qrCodeConfig.badgePosition').disable({emitEvent: false});
}
});
this.mobileAppSettingsForm.get('qrCodeConfig.qrCodeLabelEnabled').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe(value => {
if (value) {
if (value && this.mobileAppSettingsForm.get('qrCodeConfig.showOnHomePage').value) {
this.mobileAppSettingsForm.get('qrCodeConfig.qrCodeLabel').enable({emitEvent: false});
} else {
this.mobileAppSettingsForm.get('qrCodeConfig.qrCodeLabel').disable({emitEvent: false});
@ -125,10 +137,9 @@ export class MobileAppSettingsComponent extends PageComponent implements HasConf
qrCodeConfig: this.fb.group({
showOnHomePage: [true],
badgeEnabled: [true],
badgeStyle: [{value: BadgeStyle.ORIGINAL, disabled: true}],
badgePosition: [{value: BadgePosition.RIGHT, disabled: true}],
badgePosition: [BadgePosition.RIGHT],
qrCodeLabelEnabled: [true],
qrCodeLabel: ['', [Validators.required]]
qrCodeLabel: ['', [Validators.required, Validators.maxLength(50)]]
})
});
}
@ -148,7 +159,7 @@ export class MobileAppSettingsComponent extends PageComponent implements HasConf
this.mobileAppSettingsForm.get('androidConfig.appPackage').disable({emitEvent: false});
this.mobileAppSettingsForm.get('androidConfig.sha256CertFingerprints').disable({emitEvent: false});
}
this.updateBadgeControls(value);
this.mobileAppSettingsForm.get('qrCodeConfig.badgeEnabled').updateValueAndValidity({onlySelf: true});
}
private iosEnableChanged(value: boolean): void {
@ -159,31 +170,20 @@ export class MobileAppSettingsComponent extends PageComponent implements HasConf
} else {
this.mobileAppSettingsForm.get('iosConfig.appId').disable({emitEvent: false});
}
this.updateBadgeControls(value);
}
private updateBadgeControls(value: boolean) {
if (this.mobileAppSettingsForm.get('qrCodeConfig.showOnHomePage').value) {
if (value) {
this.mobileAppSettingsForm.get('qrCodeConfig.badgeEnabled').enable({emitEvent: false});
if (this.mobileAppSettingsForm.get('qrCodeConfig.badgeEnabled').value) {
this.mobileAppSettingsForm.get('qrCodeConfig.badgeStyle').enable({emitEvent: false});
this.mobileAppSettingsForm.get('qrCodeConfig.badgePosition').enable({emitEvent: false});
}
} else {
if (!this.mobileAppSettingsForm.get('iosConfig.enabled').value && !this.mobileAppSettingsForm.get('androidConfig.enabled').value) {
this.mobileAppSettingsForm.get('qrCodeConfig.badgeEnabled').disable({emitEvent: false});
this.mobileAppSettingsForm.get('qrCodeConfig.badgeStyle').disable({emitEvent: false});
this.mobileAppSettingsForm.get('qrCodeConfig.badgePosition').disable({emitEvent: false});
}
}
}
this.mobileAppSettingsForm.get('qrCodeConfig.badgeEnabled').updateValueAndValidity({onlySelf: true});
}
save(): void {
const showOnHomePagePreviousValue = this.mobileAppSettings.qrCodeConfig.showOnHomePage;
this.mobileAppSettings = {...this.mobileAppSettings, ...this.mobileAppSettingsForm.getRawValue()};
this.mobileAppService.saveMobileAppSettings(this.mobileAppSettings)
.subscribe((settings) => this.processMobileAppSettings(settings));
.subscribe((settings) => {
const showOnHomePageValue = settings.qrCodeConfig.showOnHomePage;
if (showOnHomePagePreviousValue !== showOnHomePageValue) {
this.store.dispatch(new ActionUpdateMobileQrCodeEnabled({mobileQrEnabled: showOnHomePageValue}));
}
this.processMobileAppSettings(settings);
});
}
confirmForm(): FormGroup {

5
ui-ngx/src/app/modules/home/pages/home-links/home-links-routing.module.ts

@ -33,6 +33,7 @@ import {
} from '@core/auth/auth.selectors';
import { EntityKeyType } from '@shared/models/query/query.models';
import { ResourcesService } from '@core/services/resources.service';
import { isDefinedAndNotNull } from '@core/utils';
const sysAdminHomePageJson = '/assets/dashboard/sys_admin_home_page.json';
const tenantAdminHomePageJson = '/assets/dashboard/tenant_admin_home_page.json';
@ -83,12 +84,12 @@ const applySystemParametersToHomeDashboard = (store: Store<AppState>,
}
}
}
if (params.mobileQrEnabled) {
if (isDefinedAndNotNull(params.mobileQrEnabled)) {
for (const widgetId of Object.keys(dashboard.configuration.widgets)) {
if (dashboard.configuration.widgets[widgetId].config.title === 'Select show mobile QR code') {
dashboard.configuration.widgets[widgetId].config.settings.markdownTextFunction =
(dashboard.configuration.widgets[widgetId].config.settings.markdownTextFunction as string)
.replace('\'${mobileQrEnabled}\'', String(true));
.replace(/\${mobileQrEnabled:([^}]+)}/, `\${mobileQrEnabled:${String(params.mobileQrEnabled)}}`);
break;
}
}

22
ui-ngx/src/app/shared/models/mobile-app.models.ts

@ -38,7 +38,6 @@ export interface QRCodeConfig {
showOnHomePage: boolean;
badgeEnabled: boolean;
badgePosition: BadgePosition;
badgeStyle: BadgeStyle;
qrCodeLabelEnabled: boolean;
qrCodeLabel: string;
}
@ -57,24 +56,3 @@ export const badgePositionTranslationsMap = new Map<BadgePosition, string>([
[BadgePosition.RIGHT, 'admin.mobile-app.right'],
[BadgePosition.LEFT, 'admin.mobile-app.left']
]);
export enum BadgeStyle {
ORIGINAL = 'ORIGINAL',
WHITE = 'WHITE'
}
export const badgeStyleTranslationsMap = new Map<BadgeStyle, string>([
[BadgeStyle.ORIGINAL, 'admin.mobile-app.original'],
[BadgeStyle.WHITE, 'admin.mobile-app.white']
]);
export const badgeStyleURLMap = new Map<BadgeStyle, MobileOSBadgeURL>([
[BadgeStyle.ORIGINAL, {
iOS: 'assets/android-ios-stores-badges/ios_store_en_black_badge.svg',
android: 'assets/android-ios-stores-badges/android_store_en_black_badge.svg'
}],
[BadgeStyle.WHITE, {
iOS: 'assets/android-ios-stores-badges/ios_store_en_white_badge.svg',
android: 'assets/android-ios-stores-badges/android_store_en_black_badge.svg'
}]
]);

53
ui-ngx/src/assets/android-ios-stores-badges/android_store_en_black_badge.svg

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

63
ui-ngx/src/assets/android-ios-stores-badges/ios_store_en_black_badge.svg

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 14 KiB

46
ui-ngx/src/assets/android-ios-stores-badges/ios_store_en_white_badge.svg

@ -1,46 +0,0 @@
<svg id="livetype" xmlns="http://www.w3.org/2000/svg" width="119.66407" height="40" viewBox="0 0 119.66407 40">
<title>Download_on_the_App_Store_Badge_US-UK_RGB_wht_092917</title>
<g>
<g>
<g>
<path d="M110.13477,0H9.53468c-.3667,0-.729,0-1.09473.002-.30615.002-.60986.00781-.91895.0127A13.21476,13.21476,0,0,0,5.5171.19141a6.66509,6.66509,0,0,0-1.90088.627A6.43779,6.43779,0,0,0,1.99757,1.99707,6.25844,6.25844,0,0,0,.81935,3.61816a6.60119,6.60119,0,0,0-.625,1.90332,12.993,12.993,0,0,0-.1792,2.002C.00587,7.83008.00489,8.1377,0,8.44434V31.5586c.00489.3105.00587.6113.01515.9219a12.99232,12.99232,0,0,0,.1792,2.0019,6.58756,6.58756,0,0,0,.625,1.9043A6.20778,6.20778,0,0,0,1.99757,38.001a6.27445,6.27445,0,0,0,1.61865,1.1787,6.70082,6.70082,0,0,0,1.90088.6308,13.45514,13.45514,0,0,0,2.0039.1768c.30909.0068.6128.0107.91895.0107C8.80567,40,9.168,40,9.53468,40H110.13477c.3594,0,.7246,0,1.084-.002.3047,0,.6172-.0039.9219-.0107a13.279,13.279,0,0,0,2-.1768,6.80432,6.80432,0,0,0,1.9082-.6308,6.27742,6.27742,0,0,0,1.6172-1.1787,6.39482,6.39482,0,0,0,1.1816-1.6143,6.60413,6.60413,0,0,0,.6191-1.9043,13.50643,13.50643,0,0,0,.1856-2.0019c.0039-.3106.0039-.6114.0039-.9219.0078-.3633.0078-.7246.0078-1.0938V9.53613c0-.36621,0-.72949-.0078-1.09179,0-.30664,0-.61426-.0039-.9209a13.5071,13.5071,0,0,0-.1856-2.002,6.6177,6.6177,0,0,0-.6191-1.90332,6.46619,6.46619,0,0,0-2.7988-2.7998,6.76754,6.76754,0,0,0-1.9082-.627,13.04394,13.04394,0,0,0-2-.17676c-.3047-.00488-.6172-.01074-.9219-.01269-.3594-.002-.7246-.002-1.084-.002Z"/>
<path d="M8.44483,39.125c-.30468,0-.602-.0039-.90429-.0107a12.68714,12.68714,0,0,1-1.86914-.1631,5.88381,5.88381,0,0,1-1.65674-.5479,5.40573,5.40573,0,0,1-1.397-1.0166,5.32082,5.32082,0,0,1-1.02051-1.3965,5.72186,5.72186,0,0,1-.543-1.6572,12.41351,12.41351,0,0,1-.1665-1.875c-.00634-.2109-.01464-.9131-.01464-.9131V8.44434S.88185,7.75293.8877,7.5498a12.37039,12.37039,0,0,1,.16553-1.87207,5.7555,5.7555,0,0,1,.54346-1.6621A5.37349,5.37349,0,0,1,2.61183,2.61768,5.56543,5.56543,0,0,1,4.01417,1.59521a5.82309,5.82309,0,0,1,1.65332-.54394A12.58589,12.58589,0,0,1,7.543.88721L8.44532.875H111.21387l.9131.0127a12.38493,12.38493,0,0,1,1.8584.16259,5.93833,5.93833,0,0,1,1.6709.54785,5.59374,5.59374,0,0,1,2.415,2.41993,5.76267,5.76267,0,0,1,.5352,1.64892,12.995,12.995,0,0,1,.1738,1.88721c.0029.2832.0029.5874.0029.89014.0079.375.0079.73193.0079,1.09179V30.4648c0,.3633,0,.7178-.0079,1.0752,0,.3252,0,.6231-.0039.9297a12.73126,12.73126,0,0,1-.1709,1.8535,5.739,5.739,0,0,1-.54,1.67,5.48029,5.48029,0,0,1-1.0156,1.3857,5.4129,5.4129,0,0,1-1.3994,1.0225,5.86168,5.86168,0,0,1-1.668.5498,12.54218,12.54218,0,0,1-1.8692.1631c-.2929.0068-.5996.0107-.8974.0107l-1.084.002Z" style="fill: #fff"/>
</g>
<g id="_Group_" data-name="&lt;Group&gt;">
<g id="_Group_2" data-name="&lt;Group&gt;">
<g id="_Group_3" data-name="&lt;Group&gt;">
<path id="_Path_" data-name="&lt;Path&gt;" d="M24.99671,19.88935a5.14625,5.14625,0,0,1,2.45058-4.31771,5.26776,5.26776,0,0,0-4.15039-2.24376c-1.74624-.1833-3.43913,1.04492-4.329,1.04492-.90707,0-2.27713-1.02672-3.75247-.99637a5.52735,5.52735,0,0,0-4.65137,2.8367c-2.01111,3.482-.511,8.59939,1.41551,11.414.96388,1.37823,2.09037,2.91774,3.56438,2.86315,1.4424-.05983,1.98111-.91977,3.7222-.91977,1.72494,0,2.23035.91977,3.73427.88506,1.54777-.02512,2.52292-1.38435,3.453-2.77563a11.39931,11.39931,0,0,0,1.579-3.21589A4.97284,4.97284,0,0,1,24.99671,19.88935Z"/>
<path id="_Path_2" data-name="&lt;Path&gt;" d="M22.15611,11.47681a5.06687,5.06687,0,0,0,1.159-3.62989,5.15524,5.15524,0,0,0-3.33555,1.72582,4.82131,4.82131,0,0,0-1.18934,3.4955A4.26259,4.26259,0,0,0,22.15611,11.47681Z"/>
</g>
</g>
<g>
<path d="M42.30178,27.13965h-4.7334l-1.13672,3.35645H34.42678l4.4834-12.418h2.083l4.4834,12.418H43.43752Zm-4.24316-1.54883h3.752L39.961,20.14355H39.9092Z"/>
<path d="M55.1592,25.96973c0,2.81348-1.50586,4.62109-3.77832,4.62109a3.0693,3.0693,0,0,1-2.84863-1.584h-.043v4.48438h-1.8584V21.44238h1.79883v1.50586h.03418a3.21162,3.21162,0,0,1,2.88281-1.60059C53.64455,21.34766,55.1592,23.16406,55.1592,25.96973Zm-1.91016,0c0-1.833-.94727-3.03809-2.39258-3.03809-1.41992,0-2.375,1.23047-2.375,3.03809,0,1.82422.95508,3.0459,2.375,3.0459C52.30178,29.01563,53.249,27.81934,53.249,25.96973Z"/>
<path d="M65.12453,25.96973c0,2.81348-1.50635,4.62109-3.77881,4.62109a3.0693,3.0693,0,0,1-2.84863-1.584h-.043v4.48438h-1.8584V21.44238h1.79883v1.50586h.03418a3.21162,3.21162,0,0,1,2.88281-1.60059C63.6094,21.34766,65.12453,23.16406,65.12453,25.96973Zm-1.91064,0c0-1.833-.94727-3.03809-2.39258-3.03809-1.41992,0-2.375,1.23047-2.375,3.03809,0,1.82422.95508,3.0459,2.375,3.0459C62.26662,29.01563,63.21389,27.81934,63.21389,25.96973Z"/>
<path d="M71.70949,27.03613c.1377,1.23145,1.334,2.04,2.96875,2.04,1.56641,0,2.69336-.80859,2.69336-1.91895,0-.96387-.67969-1.541-2.28906-1.93652l-1.60937-.3877c-2.28027-.55078-3.33887-1.61719-3.33887-3.34766,0-2.14258,1.86719-3.61426,4.51758-3.61426,2.625,0,4.42383,1.47168,4.48438,3.61426h-1.876c-.1123-1.23926-1.13672-1.9873-2.63379-1.9873s-2.52148.75684-2.52148,1.8584c0,.87793.6543,1.39453,2.25488,1.79l1.36816.33594c2.54785.60254,3.60547,1.626,3.60547,3.44238,0,2.32324-1.84961,3.77832-4.793,3.77832-2.75391,0-4.61328-1.4209-4.7334-3.667Z"/>
<path d="M83.34621,19.2998v2.14258h1.72168v1.47168H83.34621v4.99121c0,.77539.34473,1.13672,1.10156,1.13672a5.80752,5.80752,0,0,0,.61133-.043v1.46289a5.10351,5.10351,0,0,1-1.03223.08594c-1.833,0-2.54785-.68848-2.54785-2.44434V22.91406H80.16262V21.44238H81.479V19.2998Z"/>
<path d="M86.064,25.96973c0-2.84863,1.67773-4.63867,4.29395-4.63867,2.625,0,4.29492,1.79,4.29492,4.63867,0,2.85645-1.66113,4.63867-4.29492,4.63867C87.72512,30.6084,86.064,28.82617,86.064,25.96973Zm6.69531,0c0-1.9541-.89551-3.10742-2.40137-3.10742s-2.40137,1.16211-2.40137,3.10742c0,1.96191.89551,3.10645,2.40137,3.10645S92.7593,27.93164,92.7593,25.96973Z"/>
<path d="M96.18508,21.44238h1.77246v1.541h.043a2.1594,2.1594,0,0,1,2.17773-1.63574,2.86616,2.86616,0,0,1,.63672.06934v1.73828a2.59794,2.59794,0,0,0-.835-.1123,1.87264,1.87264,0,0,0-1.93652,2.083v5.37012h-1.8584Z"/>
<path d="M109.38332,27.83691c-.25,1.64355-1.85059,2.77148-3.89844,2.77148-2.63379,0-4.26855-1.76465-4.26855-4.5957,0-2.83984,1.64355-4.68164,4.19043-4.68164,2.50488,0,4.08008,1.7207,4.08008,4.46582v.63672h-6.39453v.1123a2.358,2.358,0,0,0,2.43555,2.56445,2.04834,2.04834,0,0,0,2.09082-1.27344Zm-6.28223-2.70215h4.52637a2.1773,2.1773,0,0,0-2.2207-2.29785A2.292,2.292,0,0,0,103.10109,25.13477Z"/>
</g>
</g>
</g>
<g id="_Group_4" data-name="&lt;Group&gt;">
<g>
<path d="M37.82619,8.731a2.63964,2.63964,0,0,1,2.80762,2.96484c0,1.90625-1.03027,3.002-2.80762,3.002H35.67092V8.731Zm-1.22852,5.123h1.125a1.87588,1.87588,0,0,0,1.96777-2.146,1.881,1.881,0,0,0-1.96777-2.13379h-1.125Z"/>
<path d="M41.68068,12.44434a2.13323,2.13323,0,1,1,4.24707,0,2.13358,2.13358,0,1,1-4.24707,0Zm3.333,0c0-.97607-.43848-1.54687-1.208-1.54687-.77246,0-1.207.5708-1.207,1.54688,0,.98389.43457,1.55029,1.207,1.55029C44.57522,13.99463,45.01369,13.42432,45.01369,12.44434Z"/>
<path d="M51.57326,14.69775h-.92187l-.93066-3.31641h-.07031l-.92676,3.31641h-.91309l-1.24121-4.50293h.90137l.80664,3.436h.06641l.92578-3.436h.85254l.92578,3.436h.07031l.80273-3.436h.88867Z"/>
<path d="M53.85354,10.19482H54.709v.71533h.06641a1.348,1.348,0,0,1,1.34375-.80225,1.46456,1.46456,0,0,1,1.55859,1.6748v2.915h-.88867V12.00586c0-.72363-.31445-1.0835-.97168-1.0835a1.03294,1.03294,0,0,0-1.0752,1.14111v2.63428h-.88867Z"/>
<path d="M59.09377,8.437h.88867v6.26074h-.88867Z"/>
<path d="M61.21779,12.44434a2.13346,2.13346,0,1,1,4.24756,0,2.1338,2.1338,0,1,1-4.24756,0Zm3.333,0c0-.97607-.43848-1.54687-1.208-1.54687-.77246,0-1.207.5708-1.207,1.54688,0,.98389.43457,1.55029,1.207,1.55029C64.11232,13.99463,64.5508,13.42432,64.5508,12.44434Z"/>
<path d="M66.4009,13.42432c0-.81055.60352-1.27783,1.6748-1.34424l1.21973-.07031v-.38867c0-.47559-.31445-.74414-.92187-.74414-.49609,0-.83984.18213-.93848.50049h-.86035c.09082-.77344.81836-1.26953,1.83984-1.26953,1.12891,0,1.76563.562,1.76563,1.51318v3.07666h-.85547v-.63281h-.07031a1.515,1.515,0,0,1-1.35254.707A1.36026,1.36026,0,0,1,66.4009,13.42432Zm2.89453-.38477v-.37646l-1.09961.07031c-.62012.0415-.90137.25244-.90137.64941,0,.40527.35156.64111.835.64111A1.0615,1.0615,0,0,0,69.29543,13.03955Z"/>
<path d="M71.34816,12.44434c0-1.42285.73145-2.32422,1.86914-2.32422a1.484,1.484,0,0,1,1.38086.79h.06641V8.437h.88867v6.26074h-.85156v-.71143h-.07031a1.56284,1.56284,0,0,1-1.41406.78564C72.0718,14.772,71.34816,13.87061,71.34816,12.44434Zm.918,0c0,.95508.4502,1.52979,1.20313,1.52979.749,0,1.21191-.583,1.21191-1.52588,0-.93848-.46777-1.52979-1.21191-1.52979C72.72121,10.91846,72.26613,11.49707,72.26613,12.44434Z"/>
<path d="M79.23,12.44434a2.13323,2.13323,0,1,1,4.24707,0,2.13358,2.13358,0,1,1-4.24707,0Zm3.333,0c0-.97607-.43848-1.54687-1.208-1.54687-.77246,0-1.207.5708-1.207,1.54688,0,.98389.43457,1.55029,1.207,1.55029C82.12453,13.99463,82.563,13.42432,82.563,12.44434Z"/>
<path d="M84.66945,10.19482h.85547v.71533h.06641a1.348,1.348,0,0,1,1.34375-.80225,1.46456,1.46456,0,0,1,1.55859,1.6748v2.915H87.605V12.00586c0-.72363-.31445-1.0835-.97168-1.0835a1.03294,1.03294,0,0,0-1.0752,1.14111v2.63428h-.88867Z"/>
<path d="M93.51516,9.07373v1.1416h.97559v.74854h-.97559V13.2793c0,.47168.19434.67822.63672.67822a2.96657,2.96657,0,0,0,.33887-.02051v.74023a2.9155,2.9155,0,0,1-.4834.04541c-.98828,0-1.38184-.34766-1.38184-1.21582v-2.543h-.71484v-.74854h.71484V9.07373Z"/>
<path d="M95.70461,8.437h.88086v2.48145h.07031a1.3856,1.3856,0,0,1,1.373-.80664,1.48339,1.48339,0,0,1,1.55078,1.67871v2.90723H98.69v-2.688c0-.71924-.335-1.0835-.96289-1.0835a1.05194,1.05194,0,0,0-1.13379,1.1416v2.62988h-.88867Z"/>
<path d="M104.76125,13.48193a1.828,1.828,0,0,1-1.95117,1.30273A2.04531,2.04531,0,0,1,100.73,12.46045a2.07685,2.07685,0,0,1,2.07617-2.35254c1.25293,0,2.00879.856,2.00879,2.27V12.688h-3.17969v.0498a1.1902,1.1902,0,0,0,1.19922,1.29,1.07934,1.07934,0,0,0,1.07129-.5459Zm-3.126-1.45117h2.27441a1.08647,1.08647,0,0,0-1.1084-1.1665A1.15162,1.15162,0,0,0,101.63527,12.03076Z"/>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 10 KiB

28
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-height: 900px) {\n .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title {\n display: none;\n }\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: 110px;\n }\n \n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-badges {\n max-width: 140px;\n }\n}\n\n@media screen and (min-width: 1819px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n max-width: 125px;\n }\n \n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-badges {\n max-width: 160px;\n }\n}",
"showTitleIcon": false,
"titleTooltip": "",
"titleStyle": null
"titleStyle": null,
"borderRadius": "12px"
},
"row": 0,
"col": 0,
@ -2670,7 +2688,7 @@
"settings": {
"useMarkdownTextFunction": true,
"markdownTextPattern": "### Markdown/HTML card\n - **Current entity**: ${entityName}.\n - **Current value**: ${Random}.",
"markdownTextFunction": "let html = '<div style=\"width: 100%; height: 100%;\">';\nlet supportMobile = '${mobileQrEnabled}' || false;\nif (supportMobile && typeof supportMobile === 'boolean') {\n ctx.dashboardWidget.widgetLayout.mobileHide = false;\n html += '<tb-dashboard-state stateId=\"mobile_app_qr_code\" [ctx]=\"ctx\"></tb-dashboard-state>';\n} else {\n ctx.dashboardWidget.widgetLayout.mobileHide = true;\n html += '<tb-getting-started-widget style=\"padding: 16px;\"></tb-getting-started-widget>';\n}\nhtml += '</div>';\nreturn html;",
"markdownTextFunction": "let html = '<div style=\"width: 100%; height: 100%;\">';\nlet supportMobile = '${mobileQrEnabled:valuePlaceholder}';\nlet supportMobileValue = supportMobile.match(/\\${mobileQrEnabled:([^}]+)}/)[1];\nif (supportMobileValue === 'true') {\n ctx.dashboardWidget.widgetLayout.mobileHide = false;\n html += '<tb-dashboard-state stateId=\"mobile_app_qr_code\" [ctx]=\"ctx\"></tb-dashboard-state>';\n} else {\n ctx.dashboardWidget.widgetLayout.mobileHide = true;\n html += '<tb-getting-started-widget style=\"padding: 16px;\"></tb-getting-started-widget>';\n}\nhtml += '</div>';\nreturn html;",
"applyDefaultMarkdownStyle": false,
"markdownCss": ""
},

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

@ -1125,7 +1125,7 @@
"settings": {
"useMarkdownTextFunction": true,
"markdownTextPattern": "### Markdown/HTML card\n - **Current entity**: ${entityName}.\n - **Current value**: ${Random}.",
"markdownTextFunction": "let html = '<div style=\"width: 100%; height: 100%;\">';\nlet supportMobile = '${mobileQrEnabled}' || false;\nif (supportMobile && typeof supportMobile === 'boolean') {\n ctx.dashboardWidget.widgetLayout.mobileHide = false;\n html += '<tb-dashboard-state stateId=\"mobile_app_qr_code\" [ctx]=\"ctx\"></tb-dashboard-state>';\n} else {\n ctx.dashboardWidget.widgetLayout.mobileHide = true;\n html += '<tb-getting-started-widget style=\"padding: 16px;\"></tb-getting-started-widget>';\n}\nhtml += '</div>';\nreturn html;",
"markdownTextFunction": "let html = '<div style=\"width: 100%; height: 100%;\">';\nlet supportMobile = '${mobileQrEnabled:valuePlaceholder}';\nlet supportMobileValue = supportMobile.match(/\\${mobileQrEnabled:([^}]+)}/)[1];\nif (supportMobileValue === 'true') {\n ctx.dashboardWidget.widgetLayout.mobileHide = false;\n html += '<tb-dashboard-state stateId=\"mobile_app_qr_code\" [ctx]=\"ctx\"></tb-dashboard-state>';\n} else {\n ctx.dashboardWidget.widgetLayout.mobileHide = true;\n html += '<tb-getting-started-widget style=\"padding: 16px;\"></tb-getting-started-widget>';\n}\nhtml += '</div>';\nreturn html;",
"applyDefaultMarkdownStyle": false,
"markdownCss": ""
},
@ -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-height: 900px) {\n .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title {\n display: none;\n }\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: 1182px) {\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: 120px;\n }\n \n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-badges {\n max-width: 160px;\n }\n}\n\n@media screen and (min-width: 1819px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n max-width: 125px;\n }\n \n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-badges {\n max-width: 160px;\n }\n}",
"showTitleIcon": false,
"titleTooltip": "",
"titleStyle": null
"titleStyle": null,
"borderRadius": "12px"
},
"row": 0,
"col": 0,
@ -1268,7 +1286,7 @@
"row": 0,
"col": 85,
"mobileHide": false,
"mobileHeight": 10
"mobileHeight": 7
}
},
"gridSettings": {

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

@ -442,10 +442,9 @@
"enabled": "Enabled",
"disabled": "Disabled",
"badges": "Badges",
"original": "Original",
"white": "White",
"label": "Label",
"label-required": "Label is required",
"label-max-length": "Label should be less than or equal to 50 characters",
"right": "Right",
"left": "Left",
"set": "Set",
@ -5565,6 +5564,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