Browse Source

Review fixes

pull/16046/head
Maksym Tsymbarov 2 weeks ago
parent
commit
19c433edd5
  1. 9
      ui-ngx/package.json
  2. 2
      ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.html
  3. 50
      ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts
  4. 2
      ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.html
  5. 12
      ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.componet.ts
  6. 2
      ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.html
  7. 44
      ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.ts
  8. 34
      ui-ngx/src/app/shared/models/hugerte/hugerte.models.ts
  9. 3
      ui-ngx/src/app/shared/shared.module.ts
  10. 25
      ui-ngx/src/styles.scss
  11. 6
      ui-ngx/yarn.lock

9
ui-ngx/package.json

@ -9,7 +9,7 @@
"build:types": "node generate-types.js", "build:types": "node generate-types.js",
"build:icon-metadata": "node generate-icon-metadata.js", "build:icon-metadata": "node generate-icon-metadata.js",
"lint": "ng lint", "lint": "ng lint",
"prepare": "patch-package" "prepare": "patch-package --error-on-fail"
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
@ -27,6 +27,7 @@
"@flowjs/flow.js": "^2.14.1", "@flowjs/flow.js": "^2.14.1",
"@flowjs/ngx-flow": "20.0.2", "@flowjs/ngx-flow": "20.0.2",
"@geoman-io/leaflet-geoman-free": "2.19.3", "@geoman-io/leaflet-geoman-free": "2.19.3",
"@hugerte/hugerte-angular": "~1.1.1",
"@iplab/ngx-color-picker": "^20.0.0", "@iplab/ngx-color-picker": "^20.0.0",
"@mat-datetimepicker/core": "~16.0.1", "@mat-datetimepicker/core": "~16.0.1",
"@mdi/svg": "^7.4.47", "@mdi/svg": "^7.4.47",
@ -37,7 +38,6 @@
"@ngx-translate/core": "^17.0.0", "@ngx-translate/core": "^17.0.0",
"@svgdotjs/svg.filter.js": "^3.0.9", "@svgdotjs/svg.filter.js": "^3.0.9",
"@svgdotjs/svg.js": "3.2.4", "@svgdotjs/svg.js": "3.2.4",
"@hugerte/hugerte-angular": "^1.1.1",
"@svgdotjs/svg.panzoom.js": "^2.1.2", "@svgdotjs/svg.panzoom.js": "^2.1.2",
"ace-builds": "1.43.6", "ace-builds": "1.43.6",
"ace-diff": "^3.3.0", "ace-diff": "^3.3.0",
@ -51,7 +51,7 @@
"flot.curvedlines": "https://github.com/MichaelZinsmaier/CurvedLines.git#master", "flot.curvedlines": "https://github.com/MichaelZinsmaier/CurvedLines.git#master",
"font-awesome": "^4.7.0", "font-awesome": "^4.7.0",
"html2canvas": "^1.4.1", "html2canvas": "^1.4.1",
"hugerte": "^1.0.12", "hugerte": "~1.0.12",
"jquery": "^3.7.1", "jquery": "^3.7.1",
"jquery.terminal": "^2.45.2", "jquery.terminal": "^2.45.2",
"js-beautify": "1.15.4", "js-beautify": "1.15.4",
@ -139,7 +139,8 @@
"resolutions": { "resolutions": {
"@babel/core": "7.29.7", "@babel/core": "7.29.7",
"esbuild": "0.28.1", "esbuild": "0.28.1",
"less": "4.9.0", "rollup": "4.59.0",
"less": ">=4.9.0",
"js-beautify/**/minimatch": "^9.0.7" "js-beautify/**/minimatch": "^9.0.7"
} }
} }

2
ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.html

@ -17,7 +17,7 @@
--> -->
<div class="tb-editor-panel"> <div class="tb-editor-panel">
<div class="tb-editor-title">{{ title | translate }}</div> <div class="tb-editor-title">{{ title | translate }}</div>
<editor class="tb-editor" [init]="tinyMceOptions" [formControl]="editorControl"></editor> <editor class="tb-editor" [init]="hugeRteOptions" [formControl]="editorControl"></editor>
<div class="tb-editor-buttons"> <div class="tb-editor-buttons">
<button mat-button <button mat-button
color="primary" color="primary"

50
ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts

@ -18,6 +18,7 @@ import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } fro
import { FormBuilder, FormControl } from '@angular/forms'; import { FormBuilder, FormControl } from '@angular/forms';
import { TbPopoverComponent } from '@shared/components/popover.component'; import { TbPopoverComponent } from '@shared/components/popover.component';
import { EditorOptions } from 'hugerte'; import { EditorOptions } from 'hugerte';
import { defaultHugeRteOptions } from '@shared/models/hugerte/hugerte.models';
@Component({ @Component({
selector: 'tb-release-notes-panel', selector: 'tb-release-notes-panel',
@ -45,52 +46,15 @@ export class EditorPanelComponent implements OnInit {
editorControl: FormControl<string>; editorControl: FormControl<string>;
tinyMceOptions: Partial<EditorOptions> = { hugeRteOptions: Partial<EditorOptions> = defaultHugeRteOptions({
base_url: '/assets/hugerte',
suffix: '.min',
plugins: ['link', 'table', 'image', 'lists', 'fullscreen'], plugins: ['link', 'table', 'image', 'lists', 'fullscreen'],
menubar: 'edit insert view format', menubar: 'edit insert view format',
toolbar: ['fontfamily fontsize | bold italic underline strikethrough forecolor backcolor', toolbar: ['fontfamily fontsize | bold italic underline strikethrough forecolor backcolor',
'alignleft aligncenter alignright alignjustify | bullist | link table image | fullscreen'], 'alignleft aligncenter alignright alignjustify | bullist | link table image | fullscreen'],
toolbar_mode: 'sliding', toolbar_mode: 'sliding',
height: 400, height: 400,
autofocus: false, resize: false
branding: false, });
resize: false,
setup: (editor) => {
editor.on('PostRender', function() {
const container = document.querySelector('.tox.tox-hugerte-aux');
const styleSheet = document.createElement('style');
styleSheet.innerText = `
.tox-tiered-menu .tox-menu {
width: fit-content;
max-width: min(80%, 440px);
@media screen and (max-width: 510px) {
max-width: calc(100% - 64px);
}
media screen and (min-width: 511px) and (max-width: 548px) {
max-width: calc(100% - 84px);
}
media screen and (min-width: 549px) and (max-width: 599px) {
max-width: calc(100% - 104px);
}
}
.tox-tiered-menu .tox-menu .tox-collection__item-label {
word-break: normal;
}
@media screen and (max-width: 890px) {
.tox-tiered-menu > .tox-collection--list:not(:first-child) {
left: auto !important;
right: 0 !important;
}
}
`;
container.prepend(styleSheet);
});
},
relative_urls: false,
urlconverter_callback: (url) => url
};
constructor(private fb: FormBuilder) { constructor(private fb: FormBuilder) {
} }
@ -99,9 +63,9 @@ export class EditorPanelComponent implements OnInit {
this.editorControl = this.fb.control(this.content); this.editorControl = this.fb.control(this.content);
if (this.disabled) { if (this.disabled) {
this.editorControl.disable({emitEvent: false}); this.editorControl.disable({emitEvent: false});
this.tinyMceOptions.toolbar = false; this.hugeRteOptions.toolbar = false;
this.tinyMceOptions.menubar = false; this.hugeRteOptions.menubar = false;
this.tinyMceOptions.statusbar = false; this.hugeRteOptions.statusbar = false;
} }
} }

2
ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.html

@ -213,7 +213,7 @@
<div class="notification-content"> <div class="notification-content">
<div class="subject">{{ preview.processedTemplates.EMAIL.subject }}</div> <div class="subject">{{ preview.processedTemplates.EMAIL.subject }}</div>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<editor [init]="tinyMceOptions" [initialValue]="preview.processedTemplates.EMAIL.body"></editor> <editor [init]="hugeRteOptions" [initialValue]="preview.processedTemplates.EMAIL.body"></editor>
</div> </div>
</section> </section>
<section class="preview-group notification tb-form-panel stroked no-gap" *ngIf="preview.processedTemplates.SLACK?.enabled"> <section class="preview-group notification tb-form-panel stroked no-gap" *ngIf="preview.processedTemplates.SLACK?.enabled">

12
ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.componet.ts

@ -47,6 +47,8 @@ import { AuthUser } from '@shared/models/user.model';
import { getCurrentAuthUser } from '@core/auth/auth.selectors'; import { getCurrentAuthUser } from '@core/auth/auth.selectors';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { EditorOptions } from 'hugerte';
import { defaultHugeRteOptions, HUGERTE_BODY_ID } from '@shared/models/hugerte/hugerte.models';
export interface RequestNotificationDialogData { export interface RequestNotificationDialogData {
request?: NotificationRequest; request?: NotificationRequest;
@ -78,9 +80,7 @@ export class SentNotificationDialogComponent extends
showRefresh = false; showRefresh = false;
tinyMceOptions: Record<string, any> = { hugeRteOptions: Partial<EditorOptions> = defaultHugeRteOptions({
base_url: '/assets/hugerte',
suffix: '.min',
plugins: ['autoresize'], plugins: ['autoresize'],
menubar: false, menubar: false,
toolbar: false, toolbar: false,
@ -88,18 +88,16 @@ export class SentNotificationDialogComponent extends
resize: false, resize: false,
readonly: true, readonly: true,
height: 400, height: 400,
autofocus: false,
branding: false,
setup: (ed) => { setup: (ed) => {
ed.on('PreInit', () => { ed.on('PreInit', () => {
const document = $(ed.iframeElement.contentDocument); const document = $(ed.iframeElement.contentDocument);
const body = $('#hugerte', document); const body = $(`#${HUGERTE_BODY_ID}`, document);
body.attr({contenteditable: false}); body.attr({contenteditable: false});
body.css('pointerEvents', 'none'); body.css('pointerEvents', 'none');
body.css('userSelect', 'none'); body.css('userSelect', 'none');
}) })
} }
}; });
private authUser: AuthUser = getCurrentAuthUser(this.store); private authUser: AuthUser = getCurrentAuthUser(this.store);

2
ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.html

@ -214,7 +214,7 @@
[class.tb-error]="(interacted || templateConfigurationForm.get('EMAIL.body').touched) && templateConfigurationForm.get('EMAIL.body').hasError('required')" [class.tb-error]="(interacted || templateConfigurationForm.get('EMAIL.body').touched) && templateConfigurationForm.get('EMAIL.body').hasError('required')"
translate>notification.message translate>notification.message
</mat-label> </mat-label>
<editor [init]="tinyMceOptions" formControlName="body"></editor> <editor [init]="hugeRteOptions" formControlName="body"></editor>
<mat-error class="tb-mat-error" <mat-error class="tb-mat-error"
*ngIf="(interacted || templateConfigurationForm.get('EMAIL.body').touched) && templateConfigurationForm.get('EMAIL.body').hasError('required')"> *ngIf="(interacted || templateConfigurationForm.get('EMAIL.body').touched) && templateConfigurationForm.get('EMAIL.body').hasError('required')">
{{ 'notification.message-required' | translate }} {{ 'notification.message-required' | translate }}

44
ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.ts

@ -38,6 +38,7 @@ import { deepClone, isDefinedAndNotNull } from '@core/utils';
import { coerceBoolean } from '@shared/decorators/coercion'; import { coerceBoolean } from '@shared/decorators/coercion';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { EditorOptions } from 'hugerte'; import { EditorOptions } from 'hugerte';
import { defaultHugeRteOptions } from '@shared/models/hugerte/hugerte.models';
@Component({ @Component({
selector: 'tb-template-configuration', selector: 'tb-template-configuration',
@ -83,52 +84,15 @@ export class NotificationTemplateConfigurationComponent implements OnDestroy, Co
readonly NotificationDeliveryMethod = NotificationDeliveryMethod; readonly NotificationDeliveryMethod = NotificationDeliveryMethod;
readonly NotificationTemplateTypeTranslateMap = NotificationTemplateTypeTranslateMap; readonly NotificationTemplateTypeTranslateMap = NotificationTemplateTypeTranslateMap;
tinyMceOptions: Partial<EditorOptions> = { hugeRteOptions: Partial<EditorOptions> = defaultHugeRteOptions({
base_url: '/assets/hugerte',
suffix: '.min',
plugins: ['link', 'table', 'image', 'lists', 'code', 'fullscreen'], plugins: ['link', 'table', 'image', 'lists', 'code', 'fullscreen'],
menubar: 'edit insert tools view format table', menubar: 'edit insert tools view format table',
toolbar: 'undo redo | fontfamily fontsize blocks | bold italic strikethrough | forecolor backcolor ' + toolbar: 'undo redo | fontfamily fontsize blocks | bold italic strikethrough | forecolor backcolor ' +
'| link table image | alignleft aligncenter alignright alignjustify ' + '| link table image | alignleft aligncenter alignright alignjustify ' +
'| numlist bullist | outdent indent | removeformat | code | fullscreen', '| numlist bullist | outdent indent | removeformat | code | fullscreen',
toolbar_mode: 'sliding', toolbar_mode: 'sliding',
height: 400, height: 400
autofocus: false, });
branding: false,
setup: (editor) => {
editor.on('PostRender', function() {
const container = document.querySelector('.tox.tox-hugerte-aux');
const styleSheet = document.createElement('style');
styleSheet.innerText = `
.tox-tiered-menu .tox-menu {
width: fit-content;
max-width: min(80%, 440px);
@media screen and (max-width: 510px) {
max-width: calc(100% - 64px);
}
media screen and (min-width: 511px) and (max-width: 548px) {
max-width: calc(100% - 84px);
}
media screen and (min-width: 549px) and (max-width: 599px) {
max-width: calc(100% - 104px);
}
}
.tox-tiered-menu .tox-menu .tox-collection__item-label {
word-break: normal;
}
@media screen and (max-width: 890px) {
.tox-tiered-menu > .tox-collection--list:not(:first-child) {
left: auto !important;
right: 0 !important;
}
}
`;
container.prepend(styleSheet);
});
},
relative_urls: false,
urlconverter_callback: (url) => url
};
private propagateChange = null; private propagateChange = null;
private readonly destroy$ = new Subject<void>(); private readonly destroy$ = new Subject<void>();

34
ui-ngx/src/app/shared/models/hugerte/hugerte.models.ts

@ -0,0 +1,34 @@
///
/// Copyright © 2016-2026 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 { EditorOptions } from 'hugerte';
export const HUGERTE_ASSETS_PATH = 'assets/hugerte';
export const HUGERTE_BODY_ID = 'hugerte';
export function defaultHugeRteOptions(overrides: Partial<EditorOptions>): Partial<EditorOptions> {
return {
base_url: '/' + HUGERTE_ASSETS_PATH,
suffix: '.min',
body_id: HUGERTE_BODY_ID,
autofocus: false,
branding: false,
relative_urls: false,
urlconverter_callback: (url) => url,
...overrides
};
}

3
ui-ngx/src/app/shared/shared.module.ts

@ -65,6 +65,7 @@ import { HotkeyModule } from 'angular2-hotkeys';
import { ColorPickerModule } from '@iplab/ngx-color-picker'; import { ColorPickerModule } from '@iplab/ngx-color-picker';
import { NgxHmCarouselComponent, NgxHmCarouselDynamicDirective, NgxHmCarouselItemDirective } from 'ngx-hm-carousel'; import { NgxHmCarouselComponent, NgxHmCarouselDynamicDirective, NgxHmCarouselItemDirective } from 'ngx-hm-carousel';
import { EditorModule, HUGERTE_SCRIPT_SRC } from '@hugerte/hugerte-angular'; import { EditorModule, HUGERTE_SCRIPT_SRC } from '@hugerte/hugerte-angular';
import { HUGERTE_ASSETS_PATH } from '@shared/models/hugerte/hugerte.models';
import { UserMenuComponent } from '@shared/components/user-menu.component'; import { UserMenuComponent } from '@shared/components/user-menu.component';
import { TruncateWithTooltipDirective } from '@shared/directives/truncate-with-tooltip.directive'; import { TruncateWithTooltipDirective } from '@shared/directives/truncate-with-tooltip.directive';
import { ContextMenuDirective } from '@shared/directives/context-menu.directive'; import { ContextMenuDirective } from '@shared/directives/context-menu.directive';
@ -259,7 +260,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
}, },
{ {
provide: HUGERTE_SCRIPT_SRC, provide: HUGERTE_SCRIPT_SRC,
useValue: 'assets/hugerte/hugerte.min.js' useValue: `${HUGERTE_ASSETS_PATH}/hugerte.min.js`
}, },
{ {
provide: MAT_DATE_LOCALE, provide: MAT_DATE_LOCALE,

25
ui-ngx/src/styles.scss

@ -1235,6 +1235,31 @@ pre.tb-highlight {
} }
} }
.tox-tiered-menu .tox-menu {
width: fit-content;
max-width: min(80%, 440px);
@media screen and (max-width: 510px) {
max-width: calc(100% - 64px);
}
@media screen and (min-width: 511px) and (max-width: 548px) {
max-width: calc(100% - 84px);
}
@media screen and (min-width: 549px) and (max-width: 599px) {
max-width: calc(100% - 104px);
}
}
.tox-tiered-menu .tox-menu .tox-collection__item-label {
word-break: normal;
}
@media screen and (max-width: 890px) {
.tox-tiered-menu > .tox-collection--list:not(:first-child) {
left: auto !important;
right: 0 !important;
}
}
.tb-toast-panel { .tb-toast-panel {
pointer-events: none !important; pointer-events: none !important;
} }

6
ui-ngx/yarn.lock

@ -1619,7 +1619,7 @@
resolved "https://registry.yarnpkg.com/@hugerte/framework-integration-shared/-/framework-integration-shared-1.2.0.tgz#75f04e4249bbb2c314d1f7a962b9c2f5fed7db80" resolved "https://registry.yarnpkg.com/@hugerte/framework-integration-shared/-/framework-integration-shared-1.2.0.tgz#75f04e4249bbb2c314d1f7a962b9c2f5fed7db80"
integrity sha512-ogcvVO0dFcb/HQAY7itj8xnmeMcHZmtMaOj9uC34ZYopnvj12TGWnfRQwbt8jZMlmoZk2z1TTLmJgvQBuH013A== integrity sha512-ogcvVO0dFcb/HQAY7itj8xnmeMcHZmtMaOj9uC34ZYopnvj12TGWnfRQwbt8jZMlmoZk2z1TTLmJgvQBuH013A==
"@hugerte/hugerte-angular@^1.1.1": "@hugerte/hugerte-angular@~1.1.1":
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/@hugerte/hugerte-angular/-/hugerte-angular-1.1.1.tgz#08b2c963d66d7a754e8cd4884b9111ca166781e7" resolved "https://registry.yarnpkg.com/@hugerte/hugerte-angular/-/hugerte-angular-1.1.1.tgz#08b2c963d66d7a754e8cd4884b9111ca166781e7"
integrity sha512-ubh6ZG3+dU6g3BkS2ZHx0FO46nti6N2CpN2gG/URFtF+pau/V59N94E7w7+8VeoPjqsFRpjB6zefRGNUoTJLOA== integrity sha512-ubh6ZG3+dU6g3BkS2ZHx0FO46nti6N2CpN2gG/URFtF+pau/V59N94E7w7+8VeoPjqsFRpjB6zefRGNUoTJLOA==
@ -6811,7 +6811,7 @@ https-proxy-agent@7.0.6, https-proxy-agent@^7.0.1:
agent-base "^7.1.2" agent-base "^7.1.2"
debug "4" debug "4"
hugerte@^1.0.12: hugerte@~1.0.12:
version "1.0.12" version "1.0.12"
resolved "https://registry.yarnpkg.com/hugerte/-/hugerte-1.0.12.tgz#84337116bd7e76c39e851a51d5dc0214af9882b8" resolved "https://registry.yarnpkg.com/hugerte/-/hugerte-1.0.12.tgz#84337116bd7e76c39e851a51d5dc0214af9882b8"
integrity sha512-/JDCdJy1C83a7+RjlfWx6EjRvxqmmHFA5Pzcq8Y0ngdBESiC+2xa4ogsqnRPeadF04Cs95YfTd1SKquDdksjRw== integrity sha512-/JDCdJy1C83a7+RjlfWx6EjRvxqmmHFA5Pzcq8Y0ngdBESiC+2xa4ogsqnRPeadF04Cs95YfTd1SKquDdksjRw==
@ -7601,7 +7601,7 @@ less-loader@12.3.0:
resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-12.3.0.tgz#d4a00361568be86a97da3df4f16954b0d4c15340" resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-12.3.0.tgz#d4a00361568be86a97da3df4f16954b0d4c15340"
integrity sha512-0M6+uYulvYIWs52y0LqN4+QM9TqWAohYSNTo4htE8Z7Cn3G/qQMEmktfHmyJT23k+20kU9zHH2wrfFXkxNLtVw== integrity sha512-0M6+uYulvYIWs52y0LqN4+QM9TqWAohYSNTo4htE8Z7Cn3G/qQMEmktfHmyJT23k+20kU9zHH2wrfFXkxNLtVw==
less@4.4.0, less@4.9.0: less@4.4.0, less@>=4.9.0:
version "4.9.0" version "4.9.0"
resolved "https://registry.yarnpkg.com/less/-/less-4.9.0.tgz#df49f940f89a1d158c315609735d3ca12da3e740" resolved "https://registry.yarnpkg.com/less/-/less-4.9.0.tgz#df49f940f89a1d158c315609735d3ca12da3e740"
integrity sha512-umRhrCH7fCi8Uj2RcwKjJdvUORTjeWqkdKx0LbcZvjIwsAVsnIAGcxHaqowPeBFBjQuWOeC/bve0AlpFzF/+SQ== integrity sha512-umRhrCH7fCi8Uj2RcwKjJdvUORTjeWqkdKx0LbcZvjIwsAVsnIAGcxHaqowPeBFBjQuWOeC/bve0AlpFzF/+SQ==

Loading…
Cancel
Save