Browse Source

resolve conflicts

pull/12226/head
mpetrov 2 years ago
parent
commit
c75786dd8b
  1. 8
      application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg
  2. 8
      application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg
  3. 8
      application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg
  4. 8
      application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg
  5. 19
      application/src/main/data/upgrade/basic/schema_update.sql
  6. 36
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java
  7. 2
      dao/src/main/resources/sql/schema-entities.sql
  8. 6
      ui-ngx/src/app/core/services/item-buffer.service.ts
  9. 2
      ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.ts
  10. 2
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts
  11. 11
      ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-button.component.html
  12. 42
      ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-button.component.ts
  13. 5
      ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-panel.component.html
  14. 7
      ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-panel.component.ts
  15. 4
      ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html
  16. 2
      ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.html
  17. 1
      ui-ngx/src/app/modules/home/components/public-api.ts
  18. 6
      ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts
  19. 8
      ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app.component.ts
  20. 2
      ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-layout.component.ts
  21. 4
      ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts
  22. 19
      ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-table-config.resolve.ts
  23. 12
      ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts
  24. 6
      ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.ts
  25. 6
      ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.html
  26. 18
      ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts
  27. 22
      ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.html
  28. 6
      ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.ts
  29. 3
      ui-ngx/src/app/shared/components/time/timeinterval.component.ts
  30. 17
      ui-ngx/src/app/shared/pipe/milliseconds-to-time-string.pipe.ts
  31. 1
      ui-ngx/src/assets/locale/locale.constant-ar_AE.json
  32. 3
      ui-ngx/src/assets/locale/locale.constant-ca_ES.json
  33. 3
      ui-ngx/src/assets/locale/locale.constant-cs_CZ.json
  34. 3
      ui-ngx/src/assets/locale/locale.constant-da_DK.json
  35. 3
      ui-ngx/src/assets/locale/locale.constant-el_GR.json
  36. 15
      ui-ngx/src/assets/locale/locale.constant-en_US.json
  37. 1
      ui-ngx/src/assets/locale/locale.constant-es_ES.json
  38. 3
      ui-ngx/src/assets/locale/locale.constant-ka_GE.json
  39. 3
      ui-ngx/src/assets/locale/locale.constant-ko_KR.json
  40. 1
      ui-ngx/src/assets/locale/locale.constant-lt_LT.json
  41. 3
      ui-ngx/src/assets/locale/locale.constant-lv_LV.json
  42. 3
      ui-ngx/src/assets/locale/locale.constant-nl_BE.json
  43. 1
      ui-ngx/src/assets/locale/locale.constant-pl_PL.json
  44. 3
      ui-ngx/src/assets/locale/locale.constant-pt_BR.json
  45. 1
      ui-ngx/src/assets/locale/locale.constant-ro_RO.json
  46. 3
      ui-ngx/src/assets/locale/locale.constant-sl_SI.json
  47. 3
      ui-ngx/src/assets/locale/locale.constant-tr_TR.json
  48. 2
      ui-ngx/src/assets/locale/locale.constant-uk_UA.json
  49. 1
      ui-ngx/src/assets/locale/locale.constant-zh_CN.json
  50. 3
      ui-ngx/src/assets/locale/locale.constant-zh_TW.json
  51. 3
      ui-ngx/tailwind.config.js

8
application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg

@ -18,12 +18,12 @@
},
{
"tag": "highCriticalScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighCriticalScale && highCriticalState !== null) {\n element.show();\n var offset = calculateOffset(highCriticalState, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null && highCriticalState !== null) {\n if (value >= highCriticalState && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighCriticalScale && highCriticalState !== null) {\n element.show();\n var offset = calculateOffset(highCriticalState, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null && highCriticalState !== null) {\n if (value >= highCriticalState) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}",
"actions": null
},
{
"tag": "highWarningScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar highWarningState = ctx.values.highWarningState;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighWarningScale && highWarningState !== null) {\n element.show();\n var offset = calculateOffset(highWarningState, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighWarningScale && value !== null) {\n if (!showHighCriticalScale) {\n highCriticalState = ctx.properties.maxValue;\n }\n \n if (highWarningState !== null && highCriticalState !== null) {\n if (value < highCriticalState && value >= highWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar highWarningState = ctx.values.highWarningState;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighWarningScale && highWarningState !== null) {\n element.show();\n var offset = calculateOffset(highWarningState, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighWarningScale && value !== null) {\n if (!showHighCriticalScale) {\n highCriticalState = Number.MAX_SAFE_INTEGER;\n }\n \n if (highWarningState !== null && highCriticalState !== null) {\n if (value < highCriticalState && value >= highWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}",
"actions": null
},
{
@ -33,12 +33,12 @@
},
{
"tag": "lowCriticalScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalValue = ctx.values.lowCriticalState;\n\nif (showLowCriticalScale && lowCriticalValue !== null) {\n element.show();\n var offset = calculateOffset(lowCriticalValue, minValue, maxValue);\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowCriticalScale && value !== null) {\n var lowCriticalScale = ctx.values.lowCriticalState;\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalValue = ctx.values.lowCriticalState;\n\nif (showLowCriticalScale && lowCriticalValue !== null) {\n element.show();\n var offset = calculateOffset(lowCriticalValue, minValue, maxValue);\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowCriticalScale && value !== null) {\n var lowCriticalScale = ctx.values.lowCriticalState;\n if (value <= lowCriticalScale) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}",
"actions": null
},
{
"tag": "lowWarningScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningState = ctx.values.lowWarningState;\nvar lowCriticalState = ctx.values.lowCriticalState;\nif (showLowWarningScale && lowWarningState !== null) {\n element.show();\n var offset = calculateOffset(lowWarningState, minValue, maxValue);\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowWarningScale && value !== null) {\n if (!showLowCriticalScale) {\n lowCriticalState = ctx.properties.minValue;\n }\n if (lowCriticalState !== null && lowWarningState !== null) {\n if (value > lowCriticalState && value <= lowWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningState = ctx.values.lowWarningState;\nvar lowCriticalState = ctx.values.lowCriticalState;\nif (showLowWarningScale && lowWarningState !== null) {\n element.show();\n var offset = calculateOffset(lowWarningState, minValue, maxValue);\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowWarningScale && value !== null) {\n if (!showLowCriticalScale) {\n lowCriticalState = Number.MIN_SAFE_INTEGER;\n }\n if (lowCriticalState !== null && lowWarningState !== null) {\n if (value > lowCriticalState && value <= lowWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}",
"actions": null
},
{

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

8
application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg

@ -18,12 +18,12 @@
},
{
"tag": "highCriticalScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighCriticalScale && highCriticalState !== null) {\n element.show();\n var offset = calculateOffset(highCriticalState, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null && highCriticalState !== null) {\n if (value >= highCriticalState && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighCriticalScale && highCriticalState !== null) {\n element.show();\n var offset = calculateOffset(highCriticalState, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null && highCriticalState !== null) {\n if (value >= highCriticalState) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}",
"actions": null
},
{
"tag": "highWarningScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar highWarningState = ctx.values.highWarningState;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighWarningScale && highWarningState !== null) {\n element.show();\n var offset = calculateOffset(highWarningState, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\nif (showHighWarningScale && value !== null) {\n if (!showHighCriticalScale) {\n highCriticalState = ctx.properties.maxValue;\n }\n if (highWarningState !== null && highCriticalState !== null) {\n if (value < highCriticalState && value >= highWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar highWarningState = ctx.values.highWarningState;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighWarningScale && highWarningState !== null) {\n element.show();\n var offset = calculateOffset(highWarningState, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\nif (showHighWarningScale && value !== null) {\n if (!showHighCriticalScale) {\n highCriticalState = Number.MAX_SAFE_INTEGER;\n }\n if (highWarningState !== null && highCriticalState !== null) {\n if (value < highCriticalState && value >= highWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}",
"actions": null
},
{
@ -33,12 +33,12 @@
},
{
"tag": "lowCriticalScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalValue = ctx.values.lowCriticalState;\n\nif (showLowCriticalScale && lowCriticalValue !== null) {\n element.show();\n var offset = calculateOffset(lowCriticalValue, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\nif (showLowCriticalScale && value !== null) {\n var lowCriticalScale = ctx.values.lowCriticalState;\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalValue = ctx.values.lowCriticalState;\n\nif (showLowCriticalScale && lowCriticalValue !== null) {\n element.show();\n var offset = calculateOffset(lowCriticalValue, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\nif (showLowCriticalScale && value !== null) {\n var lowCriticalScale = ctx.values.lowCriticalState;\n if (value <= lowCriticalScale) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}",
"actions": null
},
{
"tag": "lowWarningScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningState = ctx.values.lowWarningState;\nvar lowCriticalState = ctx.values.lowCriticalState;\nif (showLowWarningScale && lowWarningState !== null) {\n element.show();\n var offset = calculateOffset(lowWarningState, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\nif (showLowWarningScale && value !== null) {\n if (!showLowCriticalScale) {\n lowCriticalState = ctx.properties.minValue;\n }\n if (lowCriticalState !== null && lowWarningState !== null) {\n if (value > lowCriticalState && value <= lowWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningState = ctx.values.lowWarningState;\nvar lowCriticalState = ctx.values.lowCriticalState;\nif (showLowWarningScale && lowWarningState !== null) {\n element.show();\n var offset = calculateOffset(lowWarningState, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\nif (showLowWarningScale && value !== null) {\n if (!showLowCriticalScale) {\n lowCriticalState = Number.MIN_SAFE_INTEGER;\n }\n if (lowCriticalState !== null && lowWarningState !== null) {\n if (value > lowCriticalState && value <= lowWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}",
"actions": null
},
{

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

8
application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg

@ -18,12 +18,12 @@
},
{
"tag": "highCriticalScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalValue = ctx.properties.highCriticalScale;\nvar value = ctx.values.value;\n\nif (showHighCriticalScale) {\n element.show();\n var offset = calculateOffset(highCriticalValue, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (value >= highCriticalScale && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalValue = ctx.properties.highCriticalScale;\nvar value = ctx.values.value;\n\nif (showHighCriticalScale) {\n element.show();\n var offset = calculateOffset(highCriticalValue, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\nif (showHighCriticalScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (value >= highCriticalScale) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}",
"actions": null
},
{
"tag": "highWarningScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highWarningValue = ctx.properties.highWarningScale;\nvar value = ctx.values.value;\n\nif (showHighWarningScale) {\n element.show();\n var offset = calculateOffset(highWarningValue, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighWarningScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (!showHighCriticalScale) {\n highCriticalScale = ctx.properties.maxValue;\n }\n var highWarningScale = ctx.properties.highWarningScale;\n if (value < highCriticalScale && value >= highWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highWarningValue = ctx.properties.highWarningScale;\nvar value = ctx.values.value;\n\nif (showHighWarningScale) {\n element.show();\n var offset = calculateOffset(highWarningValue, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighWarningScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (!showHighCriticalScale) {\n highCriticalScale = Number.MAX_SAFE_INTEGER;\n }\n var highWarningScale = ctx.properties.highWarningScale;\n if (value < highCriticalScale && value >= highWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}",
"actions": null
},
{
@ -33,12 +33,12 @@
},
{
"tag": "lowCriticalScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalScale = ctx.properties.lowCriticalScale;\n\nif (showLowCriticalScale) {\n element.show();\n var offset = calculateOffset(lowCriticalScale, minValue, maxValue);\n var childrenElement = element.children();\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowCriticalScale && value !== null) {\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalScale = ctx.properties.lowCriticalScale;\n\nif (showLowCriticalScale) {\n element.show();\n var offset = calculateOffset(lowCriticalScale, minValue, maxValue);\n var childrenElement = element.children();\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowCriticalScale && value !== null) {\n if (value <= lowCriticalScale) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}",
"actions": null
},
{
"tag": "lowWarningScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningScale = ctx.properties.lowWarningScale;\nif (showLowWarningScale) {\n element.show();\n var offset = calculateOffset(lowWarningScale, minValue, maxValue);\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowWarningScale && value !== null) {\n var lowCriticalScale = ctx.properties.lowCriticalScale;\n if (!showLowCriticalScale) {\n lowCriticalScale = ctx.properties.minValue;\n }\n if (value > lowCriticalScale && value <= lowWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningScale = ctx.properties.lowWarningScale;\nif (showLowWarningScale) {\n element.show();\n var offset = calculateOffset(lowWarningScale, minValue, maxValue);\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowWarningScale && value !== null) {\n var lowCriticalScale = ctx.properties.lowCriticalScale;\n if (!showLowCriticalScale) {\n lowCriticalScale = Number.MIN_SAFE_INTEGER;\n }\n if (value > lowCriticalScale && value <= lowWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}",
"actions": null
},
{

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

8
application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg

@ -18,12 +18,12 @@
},
{
"tag": "highCriticalScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalScale = ctx.properties.highCriticalScale;\n\nif (showHighCriticalScale) {\n element.show();\n var offset = calculateOffset(highCriticalScale, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (value >= highCriticalScale && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalScale = ctx.properties.highCriticalScale;\n\nif (showHighCriticalScale) {\n element.show();\n var offset = calculateOffset(highCriticalScale, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (value >= highCriticalScale) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}",
"actions": null
},
{
"tag": "highWarningScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highWarningValue = ctx.properties.highWarningScale;\nif (showHighWarningScale) {\n element.show();\n var offset = calculateOffset(highWarningValue, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\nif (showHighWarningScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (!showHighCriticalScale) {\n highCriticalScale = ctx.properties.maxValue;\n }\n var highWarningScale = ctx.properties.highWarningScale;\n if (value < highCriticalScale && value >= highWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highWarningValue = ctx.properties.highWarningScale;\nif (showHighWarningScale) {\n element.show();\n var offset = calculateOffset(highWarningValue, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\nif (showHighWarningScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (!showHighCriticalScale) {\n highCriticalScale = Number.MAX_SAFE_INTEGER;\n }\n var highWarningScale = ctx.properties.highWarningScale;\n if (value < highCriticalScale && value >= highWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}",
"actions": null
},
{
@ -33,12 +33,12 @@
},
{
"tag": "lowCriticalScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalScale = ctx.properties.lowCriticalScale;\nif (showLowCriticalScale) {\n element.show();\n var offset = calculateOffset(lowCriticalScale, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\n\nif (showLowCriticalScale && value !== null) {\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalScale = ctx.properties.lowCriticalScale;\nif (showLowCriticalScale) {\n element.show();\n var offset = calculateOffset(lowCriticalScale, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\n\nif (showLowCriticalScale && value !== null) {\n if (value <= lowCriticalScale) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}",
"actions": null
},
{
"tag": "lowWarningScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningScale = ctx.properties.lowWarningScale;\nif (showLowWarningScale) {\n element.show();\n var offset = calculateOffset(lowWarningScale, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\n\nif (showLowWarningScale && value !== null) {\n var lowCriticalScale = ctx.properties.lowCriticalScale;\n if (!showLowCriticalScale) {\n lowCriticalScale = ctx.properties.minValue;\n }\n if (value > lowCriticalScale && value <= lowWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningScale = ctx.properties.lowWarningScale;\nif (showLowWarningScale) {\n element.show();\n var offset = calculateOffset(lowWarningScale, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\nif (showLowWarningScale && value !== null) {\n var lowCriticalScale = ctx.properties.lowCriticalScale;\n if (!showLowCriticalScale) {\n lowCriticalScale = Number.MIN_SAFE_INTEGER;\n }\n if (value > lowCriticalScale && value <= lowWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}",
"actions": null
},
{

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

19
application/src/main/data/upgrade/basic/schema_update.sql

@ -143,7 +143,7 @@ $$
LOOP
generatedBundleId := NULL;
-- migrate android config
IF (qrCodeRecord.android_config IS NOT NULL AND qrCodeRecord.android_config::jsonb -> 'appPackage' IS NOT NULL) THEN
IF (qrCodeRecord.android_config::jsonb ->> 'appPackage' IS NOT NULL) THEN
androidPkgName := qrCodeRecord.android_config::jsonb ->> 'appPackage';
SELECT id into androidAppId FROM mobile_app WHERE pkg_name = androidPkgName AND platform_type = 'ANDROID';
IF androidAppId IS NULL THEN
@ -154,17 +154,16 @@ $$
generatedBundleId := uuid_generate_v4();
INSERT INTO mobile_app_bundle(id, created_time, tenant_id, title, android_app_id)
VALUES (generatedBundleId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id, androidPkgName || ' (autogenerated)', androidAppId);
UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId,
android_enabled = (qrCodeRecord.android_config::jsonb ->> 'enabled')::boolean WHERE id = qrCodeRecord.id;
UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId;
ELSE
UPDATE mobile_app SET store_info = qrCodeRecord.android_config::jsonb - 'appPackage' - 'enabled' WHERE id = androidAppId;
UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.android_app_id = androidAppId),
android_enabled = (qrCodeRecord.android_config::jsonb ->> 'enabled')::boolean WHERE id = qrCodeRecord.id;
UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.android_app_id = androidAppId);
END IF;
END IF;
UPDATE qr_code_settings SET android_enabled = (qrCodeRecord.android_config::jsonb ->> 'enabled')::boolean WHERE id = qrCodeRecord.id;
-- migrate ios config
IF (qrCodeRecord.ios_config IS NOT NULL AND qrCodeRecord.ios_config::jsonb -> 'appId' IS NOT NULL) THEN
IF (qrCodeRecord.ios_config::jsonb ->> 'appId' IS NOT NULL) THEN
iosPkgName := substring(qrCodeRecord.ios_config::jsonb ->> 'appId', strpos(qrCodeRecord.ios_config::jsonb ->> 'appId', '.') + 1);
SELECT id INTO iosAppId FROM mobile_app WHERE pkg_name = iosPkgName AND platform_type = 'IOS';
IF iosAppId IS NULL THEN
@ -176,19 +175,19 @@ $$
generatedBundleId := uuid_generate_v4();
INSERT INTO mobile_app_bundle(id, created_time, tenant_id, title, ios_app_id)
VALUES (generatedBundleId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id, iosPkgName || ' (autogenerated)', iosAppId);
UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId,
ios_enabled = (qrCodeRecord.ios_config::jsonb ->> 'enabled')::boolean WHERE id = qrCodeRecord.id;
UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId;
ELSE
UPDATE mobile_app_bundle SET ios_app_id = iosAppId WHERE id = generatedBundleId;
END IF;
ELSE
UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.ios_app_id = iosAppId),
ios_enabled = (qrCodeRecord.ios_config::jsonb -> 'enabled')::boolean WHERE id = qrCodeRecord.id;
UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.ios_app_id = iosAppId);
UPDATE mobile_app SET store_info = qrCodeRecord.ios_config::jsonb - 'enabled' WHERE id = iosAppId;
END IF;
END IF;
UPDATE qr_code_settings SET ios_enabled = (qrCodeRecord.ios_config::jsonb -> 'enabled')::boolean WHERE id = qrCodeRecord.id;
END LOOP;
ALTER TABLE qr_code_settings RENAME CONSTRAINT mobile_app_settings_tenant_id_unq_key TO qr_code_settings_tenant_id_unq_key;
ALTER TABLE qr_code_settings RENAME CONSTRAINT mobile_app_settings_pkey TO qr_code_settings_pkey;
END IF;
ALTER TABLE qr_code_settings DROP COLUMN IF EXISTS android_config, DROP COLUMN IF EXISTS ios_config;
END;

36
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java

@ -413,23 +413,27 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
if (Boolean.TRUE.equals(sessionNewEvents.get(edgeId))) {
log.trace("[{}][{}] Set session new events flag to false", tenantId, edgeId.getId());
sessionNewEvents.put(edgeId, false);
processEdgeEventMigrationIfNeeded(session, edgeId);
session.processHighPriorityEvents();
Futures.addCallback(session.processEdgeEvents(), new FutureCallback<>() {
@Override
public void onSuccess(Boolean newEventsAdded) {
if (Boolean.TRUE.equals(newEventsAdded)) {
sessionNewEvents.put(edgeId, true);
processEdgeEventMigrationIfNeeded(session, edgeId);
if (Boolean.TRUE.equals(edgeEventsMigrationProcessed.get(edgeId))) {
Futures.addCallback(session.processEdgeEvents(), new FutureCallback<>() {
@Override
public void onSuccess(Boolean newEventsAdded) {
if (Boolean.TRUE.equals(newEventsAdded)) {
sessionNewEvents.put(edgeId, true);
}
scheduleEdgeEventsCheck(session);
}
@Override
public void onFailure(Throwable t) {
log.warn("[{}] Failed to process edge events for edge [{}]!", tenantId, session.getEdge().getId().getId(), t);
scheduleEdgeEventsCheck(session);
}
scheduleEdgeEventsCheck(session);
}
@Override
public void onFailure(Throwable t) {
log.warn("[{}] Failed to process edge events for edge [{}]!", tenantId, session.getEdge().getId().getId(), t);
scheduleEdgeEventsCheck(session);
}
}, ctx.getGrpcCallbackExecutorService());
}, ctx.getGrpcCallbackExecutorService());
} else {
scheduleEdgeEventsCheck(session);
}
} else {
scheduleEdgeEventsCheck(session);
}
@ -457,8 +461,6 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
scheduleEdgeEventsCheck(session);
} else if (Boolean.FALSE.equals(eventsExist)) {
edgeEventsMigrationProcessed.put(edgeId, true);
} else {
scheduleEdgeEventsCheck(session);
}
}
}

2
dao/src/main/resources/sql/schema-entities.sql

@ -896,7 +896,7 @@ CREATE TABLE IF NOT EXISTS queue_stats (
);
CREATE TABLE IF NOT EXISTS qr_code_settings (
id uuid NOT NULL CONSTRAINT mobile_app_settings_pkey PRIMARY KEY,
id uuid NOT NULL CONSTRAINT qr_code_settings_pkey PRIMARY KEY,
created_time bigint NOT NULL,
tenant_id uuid NOT NULL,
use_default_app boolean,

6
ui-ngx/src/app/core/services/item-buffer.service.ts

@ -305,7 +305,11 @@ export class ItemBufferService {
connectors: [],
additionalInfo: origNode.additionalInfo,
configuration: origNode.configuration,
debugMode: origNode.debugMode,
debugSettings: {
failuresEnabled: origNode.debugSettings?.failuresEnabled,
allEnabled: origNode.debugSettings?.allEnabled || origNode.debugSettings?.allEnabledUntil > new Date().getTime(),
allEnabledUntil: 0
},
x: origNode.x,
y: origNode.y,
name: origNode.name,

2
ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.ts

@ -105,7 +105,7 @@ export class AuditLogDetailsDialogComponent extends DialogComponent<AuditLogDeta
let newWidth = 600;
if (content && content.length > 0) {
const lines = content.split('\n');
newHeight = 17 * lines.length + 16;
newHeight = 18 * lines.length + 16;
let maxLineLength = 0;
lines.forEach((row) => {
const line = row.replace(/\t/g, ' ').replace(/\n/g, '');

2
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts

@ -874,7 +874,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
public exportDashboard($event: Event) {
if ($event) {
$event.stopPropagation();
$event.preventDefault();
}
this.importExport.exportDashboard(this.currentDashboardId);
}

11
ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-button.component.html

@ -23,10 +23,9 @@
[disabled]="disabled"
(click)="openDebugStrategyPanel($event, matButton)">
<mat-icon [color]="debugSettingsFormGroup.disabled ? 'inherit' : 'primary'">bug_report</mat-icon>
<span *ngIf="!failuresEnabled && !(isDebugAllActive$ | async)" translate>common.disabled</span>
<span *ngIf="(isDebugAllActive$ | async) && failuresEnabled" translate>debug-config.all</span>
<span *ngIf="(isDebugAllActive$ | async) && !failuresEnabled">
{{ !allEnabled ? (allEnabledUntil | durationLeft) : ('debug-config.min' | translate: { number: maxDebugModeDurationMinutes }) }}
</span>
<span *ngIf="!(isDebugAllActive$ | async) && failuresEnabled" translate>debug-config.failures</span>
@if (isDebugAllActive$ | async) {
{{ !allEnabled() ? (allEnabledUntil | durationLeft) : (maxDebugModeDuration | milliSecondsToTimeString: true : true) }}
} @else {
{{ (failuresEnabled ? 'debug-config.failures' : 'common.disabled') | translate }}
}
</button>

42
ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-button.component.ts

@ -14,18 +14,27 @@
/// limitations under the License.
///
import { ChangeDetectionStrategy, Component, forwardRef, Input, Renderer2, ViewContainerRef } from '@angular/core';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
forwardRef,
Input,
Renderer2,
signal,
ViewContainerRef
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { SharedModule } from '@shared/shared.module';
import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe';
import { TbPopoverService } from '@shared/components/popover.service';
import { MatButton } from '@angular/material/button';
import { DebugSettingsPanelComponent } from './debug-settings-panel.component';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
import { of, shareReplay, timer } from 'rxjs';
import { SECOND } from '@shared/models/time/time.models';
import { SECOND, MINUTE } from '@shared/models/time/time.models';
import { EntityDebugSettings } from '@shared/models/entity.models';
import { map, startWith, switchMap, takeWhile } from 'rxjs/operators';
import { map, switchMap, takeWhile } from 'rxjs/operators';
import { getCurrentAuthState } from '@core/auth/auth.selectors';
import { AppState } from '@core/core.state';
import { Store } from '@ngrx/store';
@ -60,11 +69,11 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor {
});
disabled = false;
allEnabled = signal(false);
isDebugAllActive$ = this.debugSettingsFormGroup.get('allEnabled').valueChanges.pipe(
startWith(null),
switchMap(() => {
if (this.allEnabled) {
isDebugAllActive$ = toObservable(this.allEnabled).pipe(
switchMap((value) => {
if (value) {
return of(true);
} else {
return timer(0, SECOND).pipe(
@ -77,7 +86,7 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor {
shareReplay(1)
);
readonly maxDebugModeDurationMinutes = getCurrentAuthState(this.store).maxDebugModeDurationMinutes;
readonly maxDebugModeDuration = getCurrentAuthState(this.store).maxDebugModeDurationMinutes * MINUTE;
private propagateChange: (settings: EntityDebugSettings) => void = () => {};
@ -86,22 +95,23 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor {
private store: Store<AppState>,
private viewContainerRef: ViewContainerRef,
private fb: FormBuilder,
private cd : ChangeDetectorRef,
) {
this.debugSettingsFormGroup.valueChanges.pipe(
takeUntilDestroyed()
).subscribe(value => {
this.propagateChange(value);
})
});
this.debugSettingsFormGroup.get('allEnabled').valueChanges.pipe(
takeUntilDestroyed()
).subscribe(value => this.allEnabled.set(value));
}
get failuresEnabled(): boolean {
return this.debugSettingsFormGroup.get('failuresEnabled').value;
}
get allEnabled(): boolean {
return this.debugSettingsFormGroup.get('allEnabled').value;
}
get allEnabledUntil(): number {
return this.debugSettingsFormGroup.get('allEnabledUntil').value;
}
@ -120,7 +130,7 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor {
this.viewContainerRef, DebugSettingsPanelComponent, 'bottom', true, null,
{
...debugSettings,
maxDebugModeDurationMinutes: this.maxDebugModeDurationMinutes,
maxDebugModeDuration: this.maxDebugModeDuration,
debugLimitsConfiguration: this.debugLimitsConfiguration
},
{},
@ -128,6 +138,7 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor {
debugStrategyPopover.tbComponentRef.instance.popover = debugStrategyPopover;
debugStrategyPopover.tbComponentRef.instance.onSettingsApplied.subscribe((settings: EntityDebugSettings) => {
this.debugSettingsFormGroup.patchValue(settings);
this.cd.markForCheck();
debugStrategyPopover.hide();
});
}
@ -141,6 +152,7 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor {
writeValue(settings: EntityDebugSettings): void {
this.debugSettingsFormGroup.patchValue(settings, {emitEvent: false});
this.allEnabled.set(settings?.allEnabled);
this.debugSettingsFormGroup.get('allEnabled').updateValueAndValidity({onlySelf: true});
}

5
ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-panel.component.html

@ -20,7 +20,7 @@
<div class="hint-container">
<div class="tb-form-hint tb-primary-fill tb-flex center">
@if (debugLimitsConfiguration) {
{{ 'debug-config.hint.main-limited' | translate: { msg: maxMessagesCount, sec: maxTimeFrameSec } }}
{{ 'debug-config.hint.main-limited' | translate: { msg: maxMessagesCount, time: (maxTimeFrameDuration | milliSecondsToTimeString: true : true) } }}
} @else {
{{ 'debug-config.hint.main' | translate }}
}
@ -35,7 +35,7 @@
<div class="align-center flex justify-between">
<mat-slide-toggle class="mat-slide" [formControl]="debugAllControl">
<div tb-hint-tooltip-icon="{{ 'debug-config.hint.all-messages' | translate }}">
{{ 'debug-config.all-messages' | translate: { time: (isDebugAllActive$ | async) && !allEnabled ? (allEnabledUntil | durationLeft) : ('debug-config.min' | translate: { number: maxDebugModeDurationMinutes }) } }}
{{ 'debug-config.all-messages' | translate: { time: (isDebugAllActive$ | async) && !allEnabled ? (allEnabledUntil | durationLeft) : (maxDebugModeDuration | milliSecondsToTimeString: true : true) } }}
</div>
</mat-slide-toggle>
<button mat-icon-button *ngIf="(isDebugAllActive$ | async) && !allEnabled"
@ -58,6 +58,7 @@
<button mat-raised-button
color="primary"
type="button"
[disabled]="(isLoading$ | async) || !onFailuresControl.dirty && !debugAllControl.dirty"
(click)="onApply()">
{{ 'action.apply' | translate }}
</button>

7
ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-panel.component.ts

@ -52,14 +52,14 @@ export class DebugSettingsPanelComponent extends PageComponent implements OnInit
@Input({ transform: booleanAttribute }) failuresEnabled = false;
@Input({ transform: booleanAttribute }) allEnabled = false;
@Input() allEnabledUntil = 0;
@Input() maxDebugModeDurationMinutes: number;
@Input() maxDebugModeDuration: number;
@Input() debugLimitsConfiguration: string;
onFailuresControl = this.fb.control(false);
debugAllControl = this.fb.control(false);
maxMessagesCount: string;
maxTimeFrameSec: string;
maxTimeFrameDuration: number;
initialAllEnabled: boolean;
isDebugAllActive$ = this.debugAllControl.valueChanges.pipe(
@ -99,7 +99,7 @@ export class DebugSettingsPanelComponent extends PageComponent implements OnInit
ngOnInit(): void {
this.maxMessagesCount = this.debugLimitsConfiguration?.split(':')[0];
this.maxTimeFrameSec = this.debugLimitsConfiguration?.split(':')[1];
this.maxTimeFrameDuration = parseInt(this.debugLimitsConfiguration?.split(':')[1]) * SECOND;
this.onFailuresControl.patchValue(this.failuresEnabled);
this.debugAllControl.patchValue(this.allEnabled);
this.initialAllEnabled = this.allEnabled || this.allEnabledUntil > new Date().getTime();
@ -128,6 +128,7 @@ export class DebugSettingsPanelComponent extends PageComponent implements OnInit
onReset(): void {
this.debugAllControl.patchValue(true);
this.debugAllControl.markAsDirty();
this.allEnabledUntil = 0;
this.cd.markForCheck();
}

4
ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html

@ -40,13 +40,13 @@
</div>
<mat-divider style="padding-bottom: 5px;"></mat-divider>
<div class="predicate-list">
<div class="flex flex-row items-stretch justify-start" style="height: 76px;"
<div class="flex flex-row items-stretch justify-start h-full min-h-19 max-h-24"
formArrayName="predicates"
*ngFor="let predicateControl of predicatesFormArray.controls; let $index = index">
<div class="filters-operation flex max-w-8% flex-full flex-row items-stretch justify-center">
<span *ngIf="$index > 0">{{ complexOperationTranslations.get(operation) | translate }}</span>
</div>
<div class="flex max-w-92% flex-full flex-col">
<div class="flex max-w-92% min-w-147.5 flex-full flex-col">
<div class="flex flex-1 flex-row items-center justify-start gap-2">
<tb-filter-predicate
class="flex-1"

2
ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.html

@ -44,7 +44,7 @@
<div class="tb-hint">{{ hintText | translate }}</div>
</div>
<div class="flex flex-col flex-1" [class.!hidden]="!dynamicMode && !onlyUserDynamicSource">
<div formGroupName="dynamicValue" class="flex flex-row items-center justify-start gap-2">
<div formGroupName="dynamicValue" class="flex flex-row justify-start gap-2">
<div class="flex max-w-35% flex-full flex-col">
<mat-form-field hideRequiredMarker class="mat-block">
<mat-select formControlName="sourceType" placeholder="{{'filter.dynamic-source-type' | translate}}">

1
ui-ngx/src/app/modules/home/components/public-api.ts

@ -20,6 +20,7 @@ export * from './widget/lib/settings/common/widget-settings-common.module';
export * from './widget/widget-components.module';
export * from './widget/config/widget-config-components.module';
export * from './widget/lib/chart/echarts-widget.models';
export * from './widget/config/widget-config.component.models';
export * from './widget/lib/table-widget.models';
export * from './widget/lib/flot-widget.models';

6
ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts

@ -23,14 +23,14 @@ import {
PosFunction,
WidgetUnitedMapSettings
} from '../map-models';
import { forkJoin, Observable, of, ReplaySubject, switchMap } from 'rxjs';
import { combineLatest, Observable, of, ReplaySubject, switchMap } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { calculateNewPointCoordinate, loadImageWithAspect } from '@home/components/widget/lib/maps/common-maps-utils';
import { WidgetContext } from '@home/models/widget-component.models';
import { DataSet, DatasourceType, FormattedData, widgetType } from '@shared/models/widget.models';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { WidgetSubscriptionOptions } from '@core/api/widget-api.models';
import { isDefinedAndNotNull, isEmptyStr, isNotEmptyStr, parseFunction, parseTbFunction } from '@core/utils';
import { isDefinedAndNotNull, isEmptyStr, isNotEmptyStr, parseTbFunction } from '@core/utils';
import { EntityDataPageLink } from '@shared/models/query/query.models';
import { ImagePipe } from '@shared/pipe/image.pipe';
import { CompiledTbFunction } from '@shared/models/js-function.models';
@ -55,7 +55,7 @@ export class ImageMap extends LeafletMap {
mapImage: this.mapImage(options)
};
forkJoin(initData).subscribe(inited => {
combineLatest(initData).subscribe(inited => {
this.posFunction = inited.posFunction;
const mapImage = inited.mapImage;
this.imageUrl = mapImage.imageUrl;

8
ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app.component.ts

@ -76,9 +76,9 @@ export class MobileAppComponent extends EntityComponent<MobileApp> {
}),
storeInfo: this.fb.group({
storeLink: [entity?.storeInfo?.storeLink ? entity.storeInfo.storeLink : '',
Validators.pattern(/^https?:\/\/play\.google\.com\/store\/apps\/details\?id=[a-zA-Z0-9._]+$/)],
Validators.pattern(/^https?:\/\/play\.google\.com\/store\/apps\/details\?id=[a-zA-Z0-9._]+(?:&[a-zA-Z0-9._-]+=[a-zA-Z0-9._%-]*)*$/)],
sha256CertFingerprints: [entity?.storeInfo?.sha256CertFingerprints ? entity.storeInfo.sha256CertFingerprints : '',
Validators.pattern(/^[A-Fa-f0-9]{2}(:[A-Fa-f0-9]{2}){1,31}$/)],
Validators.pattern(/^[A-Fa-f0-9]{2}(:[A-Fa-f0-9]{2}){31}$/)],
appId: [entity?.storeInfo?.appId ? entity.storeInfo.appId : '', Validators.pattern(/^[A-Z0-9]{10}\.[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*$/)],
}),
});
@ -89,11 +89,11 @@ export class MobileAppComponent extends EntityComponent<MobileApp> {
if (value === PlatformType.ANDROID) {
form.get('storeInfo.sha256CertFingerprints').enable({emitEvent: false});
form.get('storeInfo.appId').disable({emitEvent: false});
form.get('storeInfo.storeLink').setValidators(Validators.pattern(/^https?:\/\/play\.google\.com\/store\/apps\/details\?id=[a-zA-Z0-9._]+$/));
form.get('storeInfo.storeLink').setValidators(Validators.pattern(/^https?:\/\/play\.google\.com\/store\/apps\/details\?id=[a-zA-Z0-9._]+(?:&[a-zA-Z0-9._-]+=[a-zA-Z0-9._%-]*)*$/));
} else if (value === PlatformType.IOS) {
form.get('storeInfo.sha256CertFingerprints').disable({emitEvent: false});
form.get('storeInfo.appId').enable({emitEvent: false});
form.get('storeInfo.storeLink').setValidators(Validators.pattern(/^https?:\/\/apps\.apple\.com\/[a-z]{2}\/app\/[\w-]+\/id\d{7,10}$/));
form.get('storeInfo.storeLink').setValidators(Validators.pattern(/^https?:\/\/apps\.apple\.com\/[a-z]{2}\/app\/[\w-]+\/id\d{7,10}(?:\?[^\s]*)?$/));
}
form.get('storeInfo.storeLink').setValue('', {emitEvent: false});
});

2
ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-layout.component.ts

@ -207,7 +207,7 @@ export class MobileLayoutComponent implements ControlValueAccessor, Validator {
private updateModel() {
if (isDefaultMobilePagesConfig(this.pagesForm.value.pages as MobilePage[])) {
this.propagateChange({pages: []});
this.propagateChange(null);
} else {
this.propagateChange(this.pagesForm.value);
}

4
ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts

@ -187,7 +187,7 @@ export class MobilePageItemRowComponent implements ControlValueAccessor, OnInit,
}
} else {
this.isCustomMenuItem = true;
this.mobilePageRowForm.get('label').setValidators([Validators.required]);
this.mobilePageRowForm.get('label').addValidators([Validators.required]);
this.mobilePageRowForm.get('label').updateValueAndValidity({emitEvent: false});
}
this.updateCleanupState();
@ -270,7 +270,7 @@ export class MobilePageItemRowComponent implements ControlValueAccessor, OnInit,
private updateModel() {
this.modelValue.visible = this.mobilePageRowForm.get('visible').value;
const label = this.mobilePageRowForm.get('label').value;
const label = this.mobilePageRowForm.get('label').value.trim();
if (label) {
this.modelValue.label = label;
} else {

19
ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-table-config.resolve.ts

@ -24,13 +24,13 @@ import {
EntityTableConfig
} from '@home/models/entity/entities-table-config.models';
import { MobileAppBundleInfo } from '@shared/models/mobile-app.models';
import { ActivatedRouteSnapshot } from '@angular/router';
import { ActivatedRouteSnapshot, Router } from '@angular/router';
import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models';
import { Direction } from '@shared/models/page/sort-order';
import { MobileBundleTableHeaderComponent } from '@home/pages/mobile/bundes/mobile-bundle-table-header.component';
import { DatePipe } from '@angular/common';
import { MobileAppService } from '@core/http/mobile-app.service';
import { map, take } from 'rxjs/operators';
import { finalize, map, skip, take, takeUntil } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core';
import { EntityAction } from '@home/models/entity/entity-component.models';
import { MatDialog } from '@angular/material/dialog';
@ -52,11 +52,14 @@ export class MobileBundleTableConfigResolver {
private readonly config: EntityTableConfig<MobileAppBundleInfo> = new EntityTableConfig<MobileAppBundleInfo>();
private openingEditDialog = false;
constructor(
private datePipe: DatePipe,
private mobileAppService: MobileAppService,
private translate : TranslateService,
private dialog: MatDialog,
private router: Router,
private store: Store<AppState>
) {
this.config.selectionEnabled = false;
@ -108,9 +111,15 @@ export class MobileBundleTableConfigResolver {
this.config.handleRowClick = ($event, bundle) => {
$event?.stopPropagation();
this.mobileAppService.getMobileAppBundleInfoById(bundle.id.id).subscribe(appBundleInfo => {
this.editBundle($event, appBundleInfo);
})
if (!this.openingEditDialog) {
this.openingEditDialog = true;
this.mobileAppService.getMobileAppBundleInfoById(bundle.id.id).pipe(
takeUntil(this.router.events.pipe(skip(1))),
finalize(() => {this.openingEditDialog = false;})
).subscribe(
appBundleInfo => this.editBundle($event, appBundleInfo)
);
}
return true;
};

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

@ -17,6 +17,7 @@
import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core';
import { FormBuilder, FormControl } from '@angular/forms';
import { TbPopoverComponent } from '@shared/components/popover.component';
import { EditorOptions } from 'tinymce';
@Component({
selector: 'tb-release-notes-panel',
@ -43,7 +44,7 @@ export class EditorPanelComponent implements OnInit {
editorControl: FormControl<string>;
tinyMceOptions: Record<string, any> = {
tinyMceOptions: Partial<EditorOptions> = {
base_url: '/assets/tinymce',
suffix: '.min',
plugins: ['lists'],
@ -55,7 +56,14 @@ export class EditorPanelComponent implements OnInit {
autofocus: false,
branding: false,
promotion: false,
resize: false
resize: false,
setup: (editor) => {
editor.on('PostRender', function() {
const container = editor.getContainer();
const uiContainer = document.querySelector('.tox.tox-tinymce-aux');
container.parentNode.appendChild(uiContainer);
});
}
};
constructor(private fb: FormBuilder) {

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

@ -37,6 +37,7 @@ import { Subject } from 'rxjs';
import { deepClone, isDefinedAndNotNull } from '@core/utils';
import { coerceBoolean } from '@shared/decorators/coercion';
import { TranslateService } from '@ngx-translate/core';
import { EditorOptions } from 'tinymce';
@Component({
selector: 'tb-template-configuration',
@ -81,7 +82,7 @@ export class NotificationTemplateConfigurationComponent implements OnDestroy, Co
readonly NotificationDeliveryMethod = NotificationDeliveryMethod;
readonly NotificationTemplateTypeTranslateMap = NotificationTemplateTypeTranslateMap;
tinyMceOptions: Record<string, any> = {
tinyMceOptions: Partial<EditorOptions> = {
base_url: '/assets/tinymce',
suffix: '.min',
plugins: ['link', 'table', 'image', 'lists', 'code', 'fullscreen'],
@ -93,7 +94,8 @@ export class NotificationTemplateConfigurationComponent implements OnDestroy, Co
height: 400,
autofocus: false,
branding: false,
promotion: false
promotion: false,
relative_urls: false
};
private propagateChange = null;

6
ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.html

@ -239,10 +239,10 @@
matTooltipPosition="above">
<mat-icon>delete</mat-icon>
</button>
<button [disabled]="(isLoading$ | async) || !isDebugModeEnabled()"
<button [disabled]="(isLoading$ | async) || !isDebugSettingsEnabled()"
mat-fab color="accent" class="tb-btn-footer"
(click)="resetDebugModeInAllNodes()"
matTooltip="{{ 'rulenode.reset-debug-mode' | translate }}"
(click)="resetDebugSettingsInAllNodes()"
matTooltip="{{ 'rulenode.reset-debug-settings' | translate }}"
matTooltipPosition="above">
<mat-icon>bug_report</mat-icon>
</button>

18
ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts

@ -95,6 +95,7 @@ import { ComponentClusteringMode } from '@shared/models/component-descriptor.mod
import { MatDrawer } from '@angular/material/sidenav';
import { HttpStatusCode } from '@angular/common/http';
import { TbContextMenuEvent } from '@shared/models/jquery-event.models';
import { EntityDebugSettings } from '@shared/models/entity.models';
import Timeout = NodeJS.Timeout;
@Component({
@ -1415,17 +1416,20 @@ export class RuleChainPageComponent extends PageComponent
this.ruleChainCanvas.modelService.deleteSelected();
}
isDebugModeEnabled(): boolean {
const res = this.ruleChainModel.nodes.find((node) => node.debugMode);
isDebugSettingsEnabled(): boolean {
const res = this.ruleChainModel.nodes.find((node) => node?.debugSettings && this.isDebugSettingsActive(node.debugSettings));
return typeof res !== 'undefined';
}
resetDebugModeInAllNodes() {
resetDebugSettingsInAllNodes(): void {
let changed = false;
this.ruleChainModel.nodes.forEach((node) => {
if (node.component.type !== RuleNodeType.INPUT) {
changed = changed || node.debugMode;
node.debugMode = false;
const nodeHasActiveDebugSettings = node?.debugSettings && this.isDebugSettingsActive(node.debugSettings);
changed = changed || nodeHasActiveDebugSettings;
if (nodeHasActiveDebugSettings) {
node.debugSettings = { allEnabled: false, failuresEnabled: false, allEnabledUntil: 0 };
}
}
});
if (changed) {
@ -1433,6 +1437,10 @@ export class RuleChainPageComponent extends PageComponent
}
}
private isDebugSettingsActive(debugSettings: EntityDebugSettings): boolean {
return debugSettings.allEnabled || debugSettings.failuresEnabled || debugSettings.allEnabledUntil > new Date().getTime();
}
validate() {
setTimeout(() => {
this.isInvalid = false;

22
ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.html

@ -41,8 +41,24 @@
<mat-option *ngFor="let resource of filteredResources$ | async" [value]="resource">
<span [innerHTML]="resource.title | highlight:searchText"></span>
</mat-option>
<mat-option *ngIf="!(filteredResources$ | async)?.length" [value]="searchText">
{{ searchText }}
</mat-option>
<ng-container *ngIf="subType !== ResourceSubType.EXTENSION; else extensionNoText">
<mat-option *ngIf="!(filteredResources$ | async)?.length" [value]="null">
<div (click)="$event.stopPropagation()">
<div *ngIf="!textIsNotEmpty(searchText); else searchNotEmpty">
<span>{{ 'js-func.no-js-module-text' | translate }}</span>
</div>
<ng-template #searchNotEmpty>
<span>
{{ 'js-func.no-js-module-matching' | translate: {module: searchText | truncate: true: 6: &apos;...&apos;} }}
</span>
</ng-template>
</div>
</mat-option>
</ng-container>
<ng-template #extensionNoText>
<mat-option *ngIf="!(filteredResources$ | async)?.length" [value]="searchText">
{{ searchText }}
</mat-option>
</ng-template>
</mat-autocomplete>
</mat-form-field>

6
ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.ts

@ -78,6 +78,8 @@ export class ResourceAutocompleteComponent implements ControlValueAccessor, OnIn
@Input()
subType = ResourceSubType.EXTENSION;
ResourceSubType = ResourceSubType;
resourceFormGroup = this.fb.group({
resource: this.fb.control<string|ResourceInfo>(null)
});
@ -210,6 +212,10 @@ export class ResourceAutocompleteComponent implements ControlValueAccessor, OnIn
}
}
textIsNotEmpty(text: string): boolean {
return (text && text.length > 0);
}
private fetchResources(searchText?: string): Observable<Array<ResourceInfo>> {
this.searchText = searchText;
return this.resourceService.getResources(new PageLink(50, 0, searchText), ResourceType.JS_MODULE, this.subType, {ignoreLoading: true}).pipe(

3
ui-ngx/src/app/shared/components/time/timeinterval.component.ts

@ -191,8 +191,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor, OnCh
if (typeof this.modelValue !== 'undefined') {
const min = this.timeService.boundMinInterval(this.minValue);
const max = this.timeService.boundMaxInterval(this.maxValue);
if (this.allowedIntervals?.length ||
IntervalMath.numberValue(this.modelValue) >= min && IntervalMath.numberValue(this.modelValue) <= max) {
if (IntervalMath.numberValue(this.modelValue) >= min && IntervalMath.numberValue(this.modelValue) <= max) {
const advanced = this.allowedIntervals?.length
? !this.allowedIntervals.includes(this.modelValue)
: !this.timeService.matchesExistingInterval(this.minValue, this.maxValue, this.modelValue,

17
ui-ngx/src/app/shared/pipe/milliseconds-to-time-string.pipe.ts

@ -16,7 +16,7 @@
import { Pipe, PipeTransform } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { DAY, HOUR, MINUTE, SECOND } from '@shared/models/time/time.models';
import { DAY, HOUR, MINUTE, SECOND, YEAR } from '@shared/models/time/time.models';
@Pipe({
name: 'milliSecondsToTimeString'
@ -27,19 +27,21 @@ export class MillisecondsToTimeStringPipe implements PipeTransform {
}
transform(milliSeconds: number, shortFormat = false, onlyFirstDigit = false): string {
const { days, hours, minutes, seconds } = this.extractTimeUnits(milliSeconds);
return this.formatTimeString(days, hours, minutes, seconds, shortFormat, onlyFirstDigit);
const { years, days, hours, minutes, seconds } = this.extractTimeUnits(milliSeconds);
return this.formatTimeString(years, days, hours, minutes, seconds, shortFormat, onlyFirstDigit);
}
private extractTimeUnits(milliseconds: number): { days: number; hours: number; minutes: number; seconds: number } {
const days = Math.floor(milliseconds / DAY);
private extractTimeUnits(milliseconds: number): { years: number; days: number; hours: number; minutes: number; seconds: number } {
const years = Math.floor(milliseconds / YEAR);
const days = Math.floor((milliseconds % YEAR) / DAY);
const hours = Math.floor((milliseconds % DAY) / HOUR);
const minutes = Math.floor((milliseconds % HOUR) / MINUTE);
const seconds = Math.floor((milliseconds % MINUTE) / SECOND);
return { days, hours, minutes, seconds };
return { years, days, hours, minutes, seconds };
}
private formatTimeString(
years: number,
days: number,
hours: number,
minutes: number,
@ -48,6 +50,7 @@ export class MillisecondsToTimeStringPipe implements PipeTransform {
onlyFirstDigit: boolean
): string {
const timeUnits = [
{ value: years, key: 'years', shortKey: 'short.years' },
{ value: days, key: 'days', shortKey: 'short.days' },
{ value: hours, key: 'hours', shortKey: 'short.hours' },
{ value: minutes, key: 'minutes', shortKey: 'short.minutes' },
@ -59,7 +62,7 @@ export class MillisecondsToTimeStringPipe implements PipeTransform {
if (value > 0) {
timeString += this.translate.instant(shortFormat ? `timewindow.${shortKey}` : `timewindow.${key}`, { [key]: value });
if (onlyFirstDigit) {
return timeString;
return timeString.trim();
}
}
}

1
ui-ngx/src/assets/locale/locale.constant-ar_AE.json

@ -4602,7 +4602,6 @@
"output": "الإخراج",
"test": "اختبار",
"help": "مساعدة",
"reset-debug-mode": "إعادة تعيين وضع التصحيح في جميع العقد",
"test-with-this-message": "{{test}} مع هذه الرسالة"
},
"role": {

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

@ -3651,8 +3651,7 @@
"metadata-required": "Les entrades de metadades no poden estar buides.",
"output": "Sortida",
"test": "Test",
"help": "Ajuda",
"reset-debug-mode": "Restablir el mode de depuració a tots els nodes"
"help": "Ajuda"
},
"role": {
"role": "Rol",

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

@ -2426,8 +2426,7 @@
"metadata-required": "Záznam metadat nemůže být prázdný.",
"output": "Výstup",
"test": "Test",
"help": "Nápověda",
"reset-debug-mode": "Resetovat režim ladění na všech uzlech"
"help": "Nápověda"
},
"timezone": {
"timezone": "Časová zóna",

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

@ -2642,8 +2642,7 @@
"metadata-required": "Metadataposter må ikke være tomme.",
"output": "Output",
"test": "Test",
"help": "Hjælp",
"reset-debug-mode": "Nulstil debug-tilstand i alle knuder"
"help": "Hjælp"
},
"role": {
"role": "Rolle",

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

@ -1886,8 +1886,7 @@
"metadata-required": "Οι καταχωρίσεις μεταδεδομένων δεν μπορούν να είναι κενές.",
"output": "Απόδοση",
"test": "Τεστ",
"help": "Βοήθεια",
"reset-debug-mode": "Επαναφορά λειτουργίας εντοπισμού σφαλμάτων σε όλους τους κόμβους"
"help": "Βοήθεια"
},
"role": {
"role": "Ρόλος",

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

@ -990,15 +990,13 @@
"type-sms-sent": "SMS sent"
},
"debug-config": {
"min": "{{number}} min",
"label": "Debug configuration",
"on-failure": "Failures only (24/7)",
"all-messages": "All messages ({{time}})",
"failures": "Failures",
"all": "All",
"hint": {
"main": "All node debug messages rate limited with:",
"main-limited": "All node debug messages will be rate-limited, with a maximum of {{msg}} messages allowed per {{sec}} seconds.",
"main-limited": "All node debug messages will be rate-limited, with a maximum of {{msg}} messages allowed per {{time}}.",
"on-failure": "Save all failure debug events without time limit.",
"all-messages": "Save all debug events during time limit."
}
@ -3332,7 +3330,9 @@
"module-no-members": "Module has no exported members",
"module-load-error": "Module load error",
"source-code": "Source code",
"source-code-load-error": "Source code load error"
"source-code-load-error": "Source code load error",
"no-js-module-text": "No JS modules found",
"no-js-module-matching": "No JS modules matching '{{module}}' were found."
},
"key-val": {
"key": "Key",
@ -3550,7 +3550,7 @@
"tablet-959": "Tablet (max 959px)",
"max-element-number": "Max elements number",
"page-name": "Page name",
"page-nam-required": "Page name is required.",
"page-name-required": "Page name is required.",
"page-type": "Page type",
"pages-types": {
"dashboard": "Dashboard",
@ -3560,7 +3560,7 @@
"url": "URL",
"url-pattern": "Invalid URL",
"path": "Path",
"path-pattern": "Path pattern",
"path-pattern": "Invalid path",
"custom-page": "Custom page",
"edit-page": "Edit page",
"edit-custom-page": "Edit custom page",
@ -4293,7 +4293,7 @@
"output": "Output",
"test": "Test",
"help": "Help",
"reset-debug-mode": "Reset debug mode in all nodes",
"reset-debug-settings": "Reset debug settings in all nodes",
"test-with-this-message": "{{test}} with this message",
"queue-hint": "Select a queue for message forwarding to another queue. 'Main' queue is used by default.",
"queue-singleton-hint": "Select a queue for message forwarding in multi-instance environments. 'Main' queue is used by default."
@ -4743,6 +4743,7 @@
"sec": "{{ sec }} sec",
"sec-short": "{{ sec }}s",
"short": {
"years": "{ years, plural, =1 {1 year } other {# years } }",
"days": "{ days, plural, =1 {1 day } other {# days } }",
"hours": "{ hours, plural, =1 {1 hour } other {# hours } }",
"minutes": "{{minutes}} min ",

1
ui-ngx/src/assets/locale/locale.constant-es_ES.json

@ -3529,7 +3529,6 @@
"output": "Salida",
"test": "Test",
"help": "Ayuda",
"reset-debug-mode": "Restablecer el modo de depuración en todos los nodos",
"test-with-this-message": "{{test}} con este mensaje"
},
"timezone": {

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

@ -1413,8 +1413,7 @@
"metadata-required": "მეტამონაცემები ვერ იქნება ცარიელი",
"output": "რეზულტატი",
"test": "ტესტი",
"help": "დახმარება",
"reset-debug-mode": "Debug რეჟიმის გათიშვა ყველა ნოდისთვის"
"help": "დახმარება"
},
"tenant": {
"tenant": "ტენანტი",

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

@ -1878,8 +1878,7 @@
"metadata-required": "메타데이터 엔트리를 입력하세요.",
"output": "출력",
"test": "테스트",
"help": "도움말",
"reset-debug-mode": "모든 노드에 대해 디버그 모드 초기화"
"help": "도움말"
},
"timezone": {
"timezone": "Timezone",

1
ui-ngx/src/assets/locale/locale.constant-lt_LT.json

@ -4504,7 +4504,6 @@
"output": "Output",
"test": "Test",
"help": "Help",
"reset-debug-mode": "Reset debug mode in all nodes",
"test-with-this-message": "{{test}} with this message"
},
"role": {

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

@ -1338,8 +1338,7 @@
"metadata-required": "Metadatu ievadi nevar būt tukši.",
"output": "Izeja",
"test": "Tests",
"help": "Palīdzība",
"reset-debug-mode": "Atiestatīt atkļūdošanu visās nodēs"
"help": "Palīdzība"
},
"tenant": {
"tenant": "Īrnieks",

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

@ -4560,8 +4560,7 @@
"metadata-required": "Metagegevensvermeldingen mogen niet leeg zijn.",
"output": "Uitvoer",
"test": "Test",
"help": "Help",
"reset-debug-mode": "Foutopsporingsmodus resetten in alle rule nodes"
"help": "Help"
},
"role": {
"role": "Rol",

1
ui-ngx/src/assets/locale/locale.constant-pl_PL.json

@ -4520,7 +4520,6 @@
"output": "Wyjście",
"test": "Test",
"help": "Pomoc",
"reset-debug-mode": "Zresetuj tryb debugowania we wszystkich węzłach",
"test-with-this-message": "{{test}} with this message",
"description": "Opis"
},

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

@ -1554,8 +1554,7 @@
"metadata-required": "As entradas de metadados não podem estar em branco.",
"output": "Saída",
"test": "Teste",
"help": "Ajuda",
"reset-debug-mode": "Redefinir modo de depuração em todos os nós"
"help": "Ajuda"
},
"timezone": {
"timezone": "Fuso horário",

1
ui-ngx/src/assets/locale/locale.constant-ro_RO.json

@ -1401,7 +1401,6 @@
"output": "Ieşire",
"test": "Test",
"help": "Ajutor",
"reset-debug-mode": "Dezactivează modul depanare în toate nodurile"
},
"tenant": {
"tenant": "Locatar",

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

@ -1879,8 +1879,7 @@
"metadata-required": "Vnosi metapodatkov ne smejo biti prazni.",
"output": "Izdelek",
"test": "Test",
"help": "Pomoč",
"reset-debug-mode": "Ponastavi način za odpravljanje napak v vseh vozliščih"
"help": "Pomoč"
},
"timezone": {
"timezone": "Časovni pas",

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

@ -2447,8 +2447,7 @@
"metadata-required": "Meta veri girişleri boş bırakılamaz.",
"output": "Çıktı",
"test": "Ölçek",
"help": "Yardım et",
"reset-debug-mode": "Tüm düğümlerde hata ayıklama modunu sıfırla"
"help": "Yardım et"
},
"timezone": {
"timezone": "Saat dilimi",

2
ui-ngx/src/assets/locale/locale.constant-uk_UA.json

@ -1979,7 +1979,7 @@
"output": "Вихід",
"test": "Тест",
"help": "Допомога",
"reset-debug-mode": "Вимкнути режим налагодження у всіх правилах"
"reset-debug-settings": "Вимкнути налаштування налагодження у всіх правилах"
},
"scheduler": {
"scheduler": "Планувальник",

1
ui-ngx/src/assets/locale/locale.constant-zh_CN.json

@ -4012,7 +4012,6 @@
"output": "输出",
"test": "测试",
"help": "帮助",
"reset-debug-mode": "重置所有节点中的调试模式",
"test-with-this-message": "使用此消息进行{{test}}测试",
"queue-hint": "选择一个队列将消息转发到另一个队列,默认情况下使用'Main'队列。",
"queue-singleton-hint": "选择一个队列以在多实体中转发消息,默认情况下使用'Main'队列。"

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

@ -2768,8 +2768,7 @@
"metadata-required": "元資料項不能為空。",
"output": "輸出",
"test": "測試",
"help": "幫助",
"reset-debug-mode": "重置所有節點中的調試模式"
"help": "幫助"
},
"timezone": {
"timezone": "時區",

3
ui-ngx/tailwind.config.js

@ -103,7 +103,8 @@ module.exports = {
'25': '6.25rem',
'37.5': '9.375rem',
'62.5': '15.625rem',
'72.5': '18.125rem'
'72.5': '18.125rem',
'147.5': '36.875rem'
},
maxWidth: {
'5%': '5%',

Loading…
Cancel
Save