From 823b13f4bc506c0bccffc1cd8c659af5b6cbbc95 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 24 Jun 2024 12:58:33 +0300 Subject: [PATCH] UI: Add settings for 'Label & value card' widget. --- .../widget_types/label___value_card.json | 12 +- .../basic/basic-widget-config.module.ts | 12 +- ...bel-value-card-basic-config.component.html | 114 +++++++++++ ...label-value-card-basic-config.component.ts | 191 ++++++++++++++++++ .../label-value-card-widget.component.ts | 2 - .../label-card-widget-settings.component.ts | 3 +- ...-value-card-widget-settings.component.html | 81 ++++++++ ...el-value-card-widget-settings.component.ts | 108 ++++++++++ .../lib/settings/widget-settings.module.ts | 12 +- .../assets/locale/locale.constant-en_US.json | 4 + 10 files changed, 524 insertions(+), 15 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/widget/config/basic/cards/label-value-card-basic-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/config/basic/cards/label-value-card-basic-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-value-card-widget-settings.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-value-card-widget-settings.component.ts diff --git a/application/src/main/data/json/system/widget_types/label___value_card.json b/application/src/main/data/json/system/widget_types/label___value_card.json index 57e26d3050..e9ec9bae73 100644 --- a/application/src/main/data/json/system/widget_types/label___value_card.json +++ b/application/src/main/data/json/system/widget_types/label___value_card.json @@ -2,7 +2,7 @@ "fqn": "label_value_card", "name": "Label & value card", "deprecated": false, - "image": null, + "image": "tb-image:bGFiZWxWYWx1ZUNhcmQuc3Zn:IkxhYmVsICYgdmFsdWUgY2FyZCIgc3lzdGVtIHdpZGdldCBpbWFnZQ==;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEyIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIxMiAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGZpbHRlcj0idXJsKCNmaWx0ZXIwX2RfNDk4OF80NTA1NikiPgo8cmVjdCB4PSI2IiB5PSI1MiIgd2lkdGg9IjIwMCIgaGVpZ2h0PSI1NiIgcng9IjQiIGZpbGw9IndoaXRlIi8+CjxnIGZpbHRlcj0idXJsKCNmaWx0ZXIxX2JfNDk4OF80NTA1NikiPgo8cmVjdCB4PSIxOCIgeT0iNjQiIHdpZHRoPSIxNzYiIGhlaWdodD0iMzIiIHJ4PSI0IiBmaWxsPSJ3aGl0ZSIgZmlsbC1vcGFjaXR5PSIwLjc2Ii8+CjwvZz4KPHBhdGggZD0iTTM5LjUgODFWNzNDMzkuNSA3MS4zNCAzOC4xNiA3MCAzNi41IDcwQzM0Ljg0IDcwIDMzLjUgNzEuMzQgMzMuNSA3M1Y4MUMzMi4yOSA4MS45MSAzMS41IDgzLjM3IDMxLjUgODVDMzEuNSA4Ny43NiAzMy43NCA5MCAzNi41IDkwQzM5LjI2IDkwIDQxLjUgODcuNzYgNDEuNSA4NUM0MS41IDgzLjM3IDQwLjcxIDgxLjkxIDM5LjUgODFaTTM1LjUgNzNDMzUuNSA3Mi40NSAzNS45NSA3MiAzNi41IDcyQzM3LjA1IDcyIDM3LjUgNzIuNDUgMzcuNSA3M0gzNi41Vjc0SDM3LjVWNzZIMzYuNVY3N0gzNy41Vjc5SDM1LjVWNzNaIiBmaWxsPSIjNTQ2OUZGIi8+CjxwYXRoIGQ9Ik02MS42MDY0IDc1LjA0NjlWODVINTkuOTA0M1Y3NS4wNDY5SDYxLjYwNjRaTTY0LjczMDUgNzUuMDQ2OVY3Ni40MTQxSDU2LjgwNzZWNzUuMDQ2OUg2NC43MzA1Wk02OC43MzM0IDg1LjEzNjdDNjguMTg2NSA4NS4xMzY3IDY3LjY5MjEgODUuMDQ3OSA2Ny4yNSA4NC44NzAxQzY2LjgxMjUgODQuNjg3OCA2Ni40Mzg4IDg0LjQzNDkgNjYuMTI4OSA4NC4xMTEzQzY1LjgyMzYgODMuNzg3OCA2NS41ODg5IDgzLjQwNzIgNjUuNDI0OCA4Mi45Njk3QzY1LjI2MDcgODIuNTMyMiA2NS4xNzg3IDgyLjA2MDUgNjUuMTc4NyA4MS41NTQ3VjgxLjI4MTJDNjUuMTc4NyA4MC43MDI1IDY1LjI2MyA4MC4xNzg0IDY1LjQzMTYgNzkuNzA5QzY1LjYwMDMgNzkuMjM5NiA2NS44MzUgNzguODM4NSA2Ni4xMzU3IDc4LjUwNTlDNjYuNDM2NSA3OC4xNjg2IDY2Ljc5MiA3Ny45MTExIDY3LjIwMjEgNzcuNzMzNEM2Ny42MTIzIDc3LjU1NTcgNjguMDU2NiA3Ny40NjY4IDY4LjUzNTIgNzcuNDY2OEM2OS4wNjM4IDc3LjQ2NjggNjkuNTI2NCA3Ny41NTU3IDY5LjkyMjkgNzcuNzMzNEM3MC4zMTkzIDc3LjkxMTEgNzAuNjQ3NSA3OC4xNjE4IDcwLjkwNzIgNzguNDg1NEM3MS4xNzE1IDc4LjgwNDQgNzEuMzY3NSA3OS4xODQ5IDcxLjQ5NTEgNzkuNjI3QzcxLjYyNzMgODAuMDY5IDcxLjY5MzQgODAuNTU2NiA3MS42OTM0IDgxLjA4OThWODEuNzkzOUg2NS45Nzg1VjgwLjYxMTNINzAuMDY2NFY4MC40ODE0QzcwLjA1NzMgODAuMTg1MiA2OS45OTggNzkuOTA3MiA2OS44ODg3IDc5LjY0NzVDNjkuNzgzOSA3OS4zODc3IDY5LjYyMjEgNzkuMTc4MSA2OS40MDMzIDc5LjAxODZDNjkuMTg0NiA3OC44NTkgNjguODkyOSA3OC43NzkzIDY4LjUyODMgNzguNzc5M0M2OC4yNTQ5IDc4Ljc3OTMgNjguMDExMSA3OC44Mzg1IDY3Ljc5NjkgNzguOTU3QzY3LjU4NzIgNzkuMDcxIDY3LjQxMTggNzkuMjM3MyA2Ny4yNzA1IDc5LjQ1NjFDNjcuMTI5MiA3OS42NzQ4IDY3LjAxOTkgNzkuOTM5MSA2Ni45NDI0IDgwLjI0OUM2Ni44Njk1IDgwLjU1NDQgNjYuODMzIDgwLjg5ODQgNjYuODMzIDgxLjI4MTJWODEuNTU0N0M2Ni44MzMgODEuODc4MyA2Ni44NzYzIDgyLjE3OSA2Ni45NjI5IDgyLjQ1N0M2Ny4wNTQgODIuNzMwNSA2Ny4xODYyIDgyLjk2OTcgNjcuMzU5NCA4My4xNzQ4QzY3LjUzMjYgODMuMzc5OSA2Ny43NDIyIDgzLjU0MTcgNjcuOTg4MyA4My42NjAyQzY4LjIzNDQgODMuNzc0MSA2OC41MTQ2IDgzLjgzMTEgNjguODI5MSA4My44MzExQzY5LjIyNTYgODMuODMxMSA2OS41Nzg4IDgzLjc1MTMgNjkuODg4NyA4My41OTE4QzcwLjE5ODYgODMuNDMyMyA3MC40Njc0IDgzLjIwNjcgNzAuNjk1MyA4Mi45MTVMNzEuNTYzNSA4My43NTU5QzcxLjQwNCA4My45ODgzIDcxLjE5NjYgODQuMjExNiA3MC45NDE0IDg0LjQyNThDNzAuNjg2MiA4NC42MzU0IDcwLjM3NCA4NC44MDYzIDcwLjAwNDkgODQuOTM4NUM2OS42NDAzIDg1LjA3MDYgNjkuMjE2NSA4NS4xMzY3IDY4LjczMzQgODUuMTM2N1pNNzQuODc2IDc5LjEwNzRWODVINzMuMjI4NVY3Ny42MDM1SDc0Ljc4MDNMNzQuODc2IDc5LjEwNzRaTTc0LjYwOTQgODEuMDI4M0w3NC4wNDg4IDgxLjAyMTVDNzQuMDQ4OCA4MC41MTExIDc0LjExMjYgODAuMDM5NCA3NC4yNDAyIDc5LjYwNjRDNzQuMzY3OCA3OS4xNzM1IDc0LjU1NDcgNzguNzk3NSA3NC44MDA4IDc4LjQ3ODVDNzUuMDQ2OSA3OC4xNTQ5IDc1LjM1MjIgNzcuOTA2NiA3NS43MTY4IDc3LjczMzRDNzYuMDg1OSA3Ny41NTU3IDc2LjUxMiA3Ny40NjY4IDc2Ljk5NTEgNzcuNDY2OEM3Ny4zMzI0IDc3LjQ2NjggNzcuNjQgNzcuNTE2OSA3Ny45MTggNzcuNjE3MkM3OC4yMDA1IDc3LjcxMjkgNzguNDQ0MyA3Ny44NjU2IDc4LjY0OTQgNzguMDc1MkM3OC44NTkgNzguMjg0OCA3OS4wMTg2IDc4LjU1MzcgNzkuMTI3OSA3OC44ODE4Qzc5LjI0MTkgNzkuMjEgNzkuMjk4OCA3OS42MDY0IDc5LjI5ODggODAuMDcxM1Y4NUg3Ny42NTE0VjgwLjIxNDhDNzcuNjUxNCA3OS44NTQ4IDc3LjU5NjcgNzkuNTcyMyA3Ny40ODczIDc5LjM2NzJDNzcuMzgyNSA3OS4xNjIxIDc3LjIyOTggNzkuMDE2MyA3Ny4wMjkzIDc4LjkyOTdDNzYuODMzMyA3OC44Mzg1IDc2LjU5ODYgNzguNzkzIDc2LjMyNTIgNzguNzkzQzc2LjAxNTMgNzguNzkzIDc1Ljc1MSA3OC44NTIyIDc1LjUzMjIgNzguOTcwN0M3NS4zMTggNzkuMDg5MiA3NS4xNDI2IDc5LjI1MSA3NS4wMDU5IDc5LjQ1NjFDNzQuODY5MSA3OS42NjExIDc0Ljc2ODkgNzkuODk4MSA3NC43MDUxIDgwLjE2N0M3NC42NDEzIDgwLjQzNTkgNzQuNjA5NCA4MC43MjMgNzQuNjA5NCA4MS4wMjgzWk03OS4xOTYzIDgwLjU5MDhMNzguNDIzOCA4MC43NjE3Qzc4LjQyMzggODAuMzE1MSA3OC40ODU0IDc5Ljg5MzYgNzguNjA4NCA3OS40OTcxQzc4LjczNiA3OS4wOTYgNzguOTIwNiA3OC43NDUxIDc5LjE2MjEgNzguNDQ0M0M3OS40MDgyIDc4LjEzOSA3OS43MTEzIDc3Ljg5OTcgODAuMDcxMyA3Ny43MjY2QzgwLjQzMTMgNzcuNTUzNCA4MC44NDM4IDc3LjQ2NjggODEuMzA4NiA3Ny40NjY4QzgxLjY4NjggNzcuNDY2OCA4Mi4wMjQxIDc3LjUxOTIgODIuMzIwMyA3Ny42MjRDODIuNjIxMSA3Ny43MjQzIDgyLjg3NjMgNzcuODgzOCA4My4wODU5IDc4LjEwMjVDODMuMjk1NiA3OC4zMjEzIDgzLjQ1NTEgNzguNjA2MSA4My41NjQ1IDc4Ljk1N0M4My42NzM4IDc5LjMwMzQgODMuNzI4NSA3OS43MjI3IDgzLjcyODUgODAuMjE0OFY4NUg4Mi4wNzQyVjgwLjIwOEM4Mi4wNzQyIDc5LjgzNDMgODIuMDE5NSA3OS41NDQ5IDgxLjkxMDIgNzkuMzM5OEM4MS44MDUzIDc5LjEzNDggODEuNjU0OSA3OC45OTM1IDgxLjQ1OSA3OC45MTZDODEuMjYzIDc4LjgzNCA4MS4wMjgzIDc4Ljc5MyA4MC43NTQ5IDc4Ljc5M0M4MC40OTk3IDc4Ljc5MyA4MC4yNzQxIDc4Ljg0MDggODAuMDc4MSA3OC45MzY1Qzc5Ljg4NjcgNzkuMDI3NyA3OS43MjQ5IDc5LjE1NzYgNzkuNTkyOCA3OS4zMjYyQzc5LjQ2MDYgNzkuNDkwMiA3OS4zNjA0IDc5LjY3OTQgNzkuMjkyIDc5Ljg5MzZDNzkuMjI4MiA4MC4xMDc3IDc5LjE5NjMgODAuMzQwMiA3OS4xOTYzIDgwLjU5MDhaTTg3LjMyODEgNzkuMDI1NFY4Ny44NDM4SDg1LjY4MDdWNzcuNjAzNUg4Ny4xOTgyTDg3LjMyODEgNzkuMDI1NFpNOTIuMTQ3NSA4MS4yMzM0VjgxLjM3N0M5Mi4xNDc1IDgxLjkxNDcgOTIuMDgzNyA4Mi40MTM3IDkxLjk1NjEgODIuODc0QzkxLjgzMyA4My4zMjk4IDkxLjY0ODQgODMuNzI4NSA5MS40MDIzIDg0LjA3MDNDOTEuMTYwOCA4NC40MDc2IDkwLjg2MjMgODQuNjY5NiA5MC41MDY4IDg0Ljg1NjRDOTAuMTUxNCA4NS4wNDMzIDg5Ljc0MTIgODUuMTM2NyA4OS4yNzY0IDg1LjEzNjdDODguODE2MSA4NS4xMzY3IDg4LjQxMjggODUuMDUyNCA4OC4wNjY0IDg0Ljg4MzhDODcuNzI0NiA4NC43MTA2IDg3LjQzNTIgODQuNDY2OCA4Ny4xOTgyIDg0LjE1MjNDODYuOTYxMyA4My44Mzc5IDg2Ljc2OTkgODMuNDY4OCA4Ni42MjQgODMuMDQ0OUM4Ni40ODI3IDgyLjYxNjUgODYuMzgyNSA4Mi4xNDcxIDg2LjMyMzIgODEuNjM2N1Y4MS4wODNDODYuMzgyNSA4MC41NDA3IDg2LjQ4MjcgODAuMDQ4NSA4Ni42MjQgNzkuNjA2NEM4Ni43Njk5IDc5LjE2NDQgODYuOTYxMyA3OC43ODM5IDg3LjE5ODIgNzguNDY0OEM4Ny40MzUyIDc4LjE0NTggODcuNzI0NiA3Ny44OTk3IDg4LjA2NjQgNzcuNzI2NkM4OC40MDgyIDc3LjU1MzQgODguODA3IDc3LjQ2NjggODkuMjYyNyA3Ny40NjY4Qzg5LjcyNzUgNzcuNDY2OCA5MC4xNCA3Ny41NTc5IDkwLjUgNzcuNzQwMkM5MC44NiA3Ny45MTggOTEuMTYzMSA3OC4xNzMyIDkxLjQwOTIgNzguNTA1OUM5MS42NTUzIDc4LjgzNCA5MS44Mzk4IDc5LjIzMDUgOTEuOTYyOSA3OS42OTUzQzkyLjA4NTkgODAuMTU1NiA5Mi4xNDc1IDgwLjY2ODMgOTIuMTQ3NSA4MS4yMzM0Wk05MC41IDgxLjM3N1Y4MS4yMzM0QzkwLjUgODAuODkxNiA5MC40NjgxIDgwLjU3NDkgOTAuNDA0MyA4MC4yODMyQzkwLjM0MDUgNzkuOTg3IDkwLjI0MDIgNzkuNzI3MiA5MC4xMDM1IDc5LjUwMzlDODkuOTY2OCA3OS4yODA2IDg5Ljc5MTMgNzkuMTA3NCA4OS41NzcxIDc4Ljk4NDRDODkuMzY3NSA3OC44NTY4IDg5LjExNDYgNzguNzkzIDg4LjgxODQgNzguNzkzQzg4LjUyNjcgNzguNzkzIDg4LjI3NiA3OC44NDMxIDg4LjA2NjQgNzguOTQzNEM4Ny44NTY4IDc5LjAzOTEgODcuNjgxMyA3OS4xNzM1IDg3LjU0IDc5LjM0NjdDODcuMzk4OCA3OS41MTk5IDg3LjI4OTQgNzkuNzIyNyA4Ny4yMTE5IDc5Ljk1NTFDODcuMTM0NCA4MC4xODI5IDg3LjA3OTggODAuNDMxMyA4Ny4wNDc5IDgwLjcwMDJWODIuMDI2NEM4Ny4xMDI1IDgyLjM1NDUgODcuMTk2IDgyLjY1NTMgODcuMzI4MSA4Mi45Mjg3Qzg3LjQ2MDMgODMuMjAyMSA4Ny42NDcxIDgzLjQyMDkgODcuODg4NyA4My41ODVDODguMTM0OCA4My43NDQ1IDg4LjQ0OTIgODMuODI0MiA4OC44MzIgODMuODI0MkM4OS4xMjgzIDgzLjgyNDIgODkuMzgxMiA4My43NjA0IDg5LjU5MDggODMuNjMyOEM4OS44MDA1IDgzLjUwNTIgODkuOTcxNCA4My4zMjk4IDkwLjEwMzUgODMuMTA2NEM5MC4yNDAyIDgyLjg3ODYgOTAuMzQwNSA4Mi42MTY1IDkwLjQwNDMgODIuMzIwM0M5MC40NjgxIDgyLjAyNDEgOTAuNSA4MS43MDk2IDkwLjUgODEuMzc3Wk05Ny4wNTk2IDg1LjEzNjdDOTYuNTEyNyA4NS4xMzY3IDk2LjAxODIgODUuMDQ3OSA5NS41NzYyIDg0Ljg3MDFDOTUuMTM4NyA4NC42ODc4IDk0Ljc2NSA4NC40MzQ5IDk0LjQ1NTEgODQuMTExM0M5NC4xNDk3IDgzLjc4NzggOTMuOTE1IDgzLjQwNzIgOTMuNzUxIDgyLjk2OTdDOTMuNTg2OSA4Mi41MzIyIDkzLjUwNDkgODIuMDYwNSA5My41MDQ5IDgxLjU1NDdWODEuMjgxMkM5My41MDQ5IDgwLjcwMjUgOTMuNTg5MiA4MC4xNzg0IDkzLjc1NzggNzkuNzA5QzkzLjkyNjQgNzkuMjM5NiA5NC4xNjExIDc4LjgzODUgOTQuNDYxOSA3OC41MDU5Qzk0Ljc2MjcgNzguMTY4NiA5NS4xMTgyIDc3LjkxMTEgOTUuNTI4MyA3Ny43MzM0Qzk1LjkzODUgNzcuNTU1NyA5Ni4zODI4IDc3LjQ2NjggOTYuODYxMyA3Ny40NjY4Qzk3LjM5IDc3LjQ2NjggOTcuODUyNSA3Ny41NTU3IDk4LjI0OSA3Ny43MzM0Qzk4LjY0NTUgNzcuOTExMSA5OC45NzM2IDc4LjE2MTggOTkuMjMzNCA3OC40ODU0Qzk5LjQ5NzcgNzguODA0NCA5OS42OTM3IDc5LjE4NDkgOTkuODIxMyA3OS42MjdDOTkuOTUzNSA4MC4wNjkgMTAwLjAyIDgwLjU1NjYgMTAwLjAyIDgxLjA4OThWODEuNzkzOUg5NC4zMDQ3VjgwLjYxMTNIOTguMzkyNlY4MC40ODE0Qzk4LjM4MzUgODAuMTg1MiA5OC4zMjQyIDc5LjkwNzIgOTguMjE0OCA3OS42NDc1Qzk4LjExIDc5LjM4NzcgOTcuOTQ4MiA3OS4xNzgxIDk3LjcyOTUgNzkuMDE4NkM5Ny41MTA3IDc4Ljg1OSA5Ny4yMTkxIDc4Ljc3OTMgOTYuODU0NSA3OC43NzkzQzk2LjU4MTEgNzguNzc5MyA5Ni4zMzcyIDc4LjgzODUgOTYuMTIzIDc4Ljk1N0M5NS45MTM0IDc5LjA3MSA5NS43MzggNzkuMjM3MyA5NS41OTY3IDc5LjQ1NjFDOTUuNDU1NCA3OS42NzQ4IDk1LjM0NiA3OS45MzkxIDk1LjI2ODYgODAuMjQ5Qzk1LjE5NTYgODAuNTU0NCA5NS4xNTkyIDgwLjg5ODQgOTUuMTU5MiA4MS4yODEyVjgxLjU1NDdDOTUuMTU5MiA4MS44NzgzIDk1LjIwMjUgODIuMTc5IDk1LjI4OTEgODIuNDU3Qzk1LjM4MDIgODIuNzMwNSA5NS41MTI0IDgyLjk2OTcgOTUuNjg1NSA4My4xNzQ4Qzk1Ljg1ODcgODMuMzc5OSA5Ni4wNjg0IDgzLjU0MTcgOTYuMzE0NSA4My42NjAyQzk2LjU2MDUgODMuNzc0MSA5Ni44NDA4IDgzLjgzMTEgOTcuMTU1MyA4My44MzExQzk3LjU1MTggODMuODMxMSA5Ny45MDQ5IDgzLjc1MTMgOTguMjE0OCA4My41OTE4Qzk4LjUyNDcgODMuNDMyMyA5OC43OTM2IDgzLjIwNjcgOTkuMDIxNSA4Mi45MTVMOTkuODg5NiA4My43NTU5Qzk5LjczMDEgODMuOTg4MyA5OS41MjI4IDg0LjIxMTYgOTkuMjY3NiA4NC40MjU4Qzk5LjAxMjQgODQuNjM1NCA5OC43MDAyIDg0LjgwNjMgOTguMzMxMSA4NC45Mzg1Qzk3Ljk2NjUgODUuMDcwNiA5Ny41NDI2IDg1LjEzNjcgOTcuMDU5NiA4NS4xMzY3Wk0xMDMuMjA5IDc5LjAxMTdWODVIMTAxLjU2MlY3Ny42MDM1SDEwMy4xMzRMMTAzLjIwOSA3OS4wMTE3Wk0xMDUuNDcyIDc3LjU1NTdMMTA1LjQ1OCA3OS4wODY5QzEwNS4zNTggNzkuMDY4NyAxMDUuMjQ4IDc5LjA1NSAxMDUuMTMgNzkuMDQ1OUMxMDUuMDE2IDc5LjAzNjggMTA0LjkwMiA3OS4wMzIyIDEwNC43ODggNzkuMDMyMkMxMDQuNTA2IDc5LjAzMjIgMTA0LjI1NyA3OS4wNzMyIDEwNC4wNDMgNzkuMTU1M0MxMDMuODI5IDc5LjIzMjcgMTAzLjY0OSA3OS4zNDY3IDEwMy41MDMgNzkuNDk3MUMxMDMuMzYyIDc5LjY0MjkgMTAzLjI1MiA3OS44MjA2IDEwMy4xNzUgODAuMDMwM0MxMDMuMDk3IDgwLjIzOTkgMTAzLjA1MiA4MC40NzQ2IDEwMy4wMzggODAuNzM0NEwxMDIuNjYyIDgwLjc2MTdDMTAyLjY2MiA4MC4yOTY5IDEwMi43MDggNzkuODY2MiAxMDIuNzk5IDc5LjQ2OTdDMTAyLjg5IDc5LjA3MzIgMTAzLjAyNyA3OC43MjQ2IDEwMy4yMDkgNzguNDIzOEMxMDMuMzk2IDc4LjEyMyAxMDMuNjI4IDc3Ljg4ODMgMTAzLjkwNiA3Ny43MTk3QzEwNC4xODkgNzcuNTUxMSAxMDQuNTE1IDc3LjQ2NjggMTA0Ljg4NCA3Ny40NjY4QzEwNC45ODQgNzcuNDY2OCAxMDUuMDkxIDc3LjQ3NTkgMTA1LjIwNSA3Ny40OTQxQzEwNS4zMjQgNzcuNTEyNCAxMDUuNDEyIDc3LjUzMjkgMTA1LjQ3MiA3Ny41NTU3Wk0xMTAuNjc4IDgzLjUxNjZWNzkuOTg5M0MxMTAuNjc4IDc5LjcyNDkgMTEwLjYzIDc5LjQ5NzEgMTEwLjUzNCA3OS4zMDU3QzExMC40MzggNzkuMTE0MyAxMTAuMjkzIDc4Ljk2NjEgMTEwLjA5NyA3OC44NjEzQzEwOS45MDUgNzguNzU2NSAxMDkuNjY0IDc4LjcwNDEgMTA5LjM3MiA3OC43MDQxQzEwOS4xMDMgNzguNzA0MSAxMDguODcxIDc4Ljc0OTcgMTA4LjY3NSA3OC44NDA4QzEwOC40NzkgNzguOTMyIDEwOC4zMjYgNzkuMDU1IDEwOC4yMTcgNzkuMjFDMTA4LjEwNyA3OS4zNjQ5IDEwOC4wNTMgNzkuNTQwNCAxMDguMDUzIDc5LjczNjNIMTA2LjQxMkMxMDYuNDEyIDc5LjQ0NDcgMTA2LjQ4MyA3OS4xNjIxIDEwNi42MjQgNzguODg4N0MxMDYuNzY1IDc4LjYxNTIgMTA2Ljk3IDc4LjM3MTQgMTA3LjIzOSA3OC4xNTcyQzEwNy41MDggNzcuOTQzIDEwNy44MjkgNzcuNzc0NCAxMDguMjAzIDc3LjY1MTRDMTA4LjU3NyA3Ny41MjgzIDEwOC45OTYgNzcuNDY2OCAxMDkuNDYxIDc3LjQ2NjhDMTEwLjAxNyA3Ny40NjY4IDExMC41MDkgNzcuNTYwMiAxMTAuOTM4IDc3Ljc0NzFDMTExLjM3IDc3LjkzMzkgMTExLjcxIDc4LjIxNjUgMTExLjk1NiA3OC41OTQ3QzExMi4yMDcgNzguOTY4NCAxMTIuMzMyIDc5LjQzNzggMTEyLjMzMiA4MC4wMDI5VjgzLjI5MUMxMTIuMzMyIDgzLjYyODMgMTEyLjM1NSA4My45MzEzIDExMi40IDg0LjIwMDJDMTEyLjQ1MSA4NC40NjQ1IDExMi41MjEgODQuNjk0NyAxMTIuNjEyIDg0Ljg5MDZWODVIMTEwLjkyNEMxMTAuODQ2IDg0LjgyMjMgMTEwLjc4NSA4NC41OTY3IDExMC43MzkgODQuMzIzMkMxMTAuNjk4IDg0LjA0NTIgMTEwLjY3OCA4My43NzY0IDExMC42NzggODMuNTE2NlpNMTEwLjkxNyA4MC41MDJMMTEwLjkzMSA4MS41MjA1SDEwOS43NDhDMTA5LjQ0MyA4MS41MjA1IDEwOS4xNzQgODEuNTUwMSAxMDguOTQxIDgxLjYwOTRDMTA4LjcwOSA4MS42NjQxIDEwOC41MTUgODEuNzQ2MSAxMDguMzYgODEuODU1NUMxMDguMjA1IDgxLjk2NDggMTA4LjA4OSA4Mi4wOTcgMTA4LjAxMiA4Mi4yNTJDMTA3LjkzNCA4Mi40MDY5IDEwNy44OTYgODIuNTgyNCAxMDcuODk2IDgyLjc3ODNDMTA3Ljg5NiA4Mi45NzQzIDEwNy45NDEgODMuMTU0MyAxMDguMDMyIDgzLjMxODRDMTA4LjEyMyA4My40Nzc5IDEwOC4yNTYgODMuNjAzMiAxMDguNDI5IDgzLjY5NDNDMTA4LjYwNiA4My43ODU1IDEwOC44MjEgODMuODMxMSAxMDkuMDcxIDgzLjgzMTFDMTA5LjQwOSA4My44MzExIDEwOS43MDIgODMuNzYyNyAxMDkuOTUzIDgzLjYyNkMxMTAuMjA4IDgzLjQ4NDcgMTEwLjQwOSA4My4zMTM4IDExMC41NTUgODMuMTEzM0MxMTAuNzAxIDgyLjkwODIgMTEwLjc3OCA4Mi43MTQ1IDExMC43ODcgODIuNTMyMkwxMTEuMzIgODMuMjYzN0MxMTEuMjY2IDgzLjQ1MDUgMTExLjE3MiA4My42NTEgMTExLjA0IDgzLjg2NTJDMTEwLjkwOCA4NC4wNzk0IDExMC43MzUgODQuMjg0NSAxMTAuNTIxIDg0LjQ4MDVDMTEwLjMxMSA4NC42NzE5IDExMC4wNTggODQuODI5MSAxMDkuNzYyIDg0Ljk1MjFDMTA5LjQ3IDg1LjA3NTIgMTA5LjEzMyA4NS4xMzY3IDEwOC43NSA4NS4xMzY3QzEwOC4yNjcgODUuMTM2NyAxMDcuODM2IDg1LjA0MSAxMDcuNDU4IDg0Ljg0OTZDMTA3LjA4IDg0LjY1MzYgMTA2Ljc4NCA4NC4zOTE2IDEwNi41NjkgODQuMDYzNUMxMDYuMzU1IDgzLjczMDggMTA2LjI0OCA4My4zNTQ4IDEwNi4yNDggODIuOTM1NUMxMDYuMjQ4IDgyLjU0MzYgMTA2LjMyMSA4Mi4xOTczIDEwNi40NjcgODEuODk2NUMxMDYuNjE3IDgxLjU5MTEgMTA2LjgzNiA4MS4zMzU5IDEwNy4xMjMgODEuMTMwOUMxMDcuNDE1IDgwLjkyNTggMTA3Ljc3IDgwLjc3MDggMTA4LjE4OSA4MC42NjZDMTA4LjYwOSA4MC41NTY2IDEwOS4wODcgODAuNTAyIDEwOS42MjUgODAuNTAySDExMC45MTdaTTExNy42OTUgNzcuNjAzNVY3OC44MDY2SDExMy41MjVWNzcuNjAzNUgxMTcuNjk1Wk0xMTQuNzI5IDc1Ljc5MkgxMTYuMzc2VjgyLjk1NjFDMTE2LjM3NiA4My4xODM5IDExNi40MDggODMuMzU5NCAxMTYuNDcyIDgzLjQ4MjRDMTE2LjU0IDgzLjYwMDkgMTE2LjYzMyA4My42ODA3IDExNi43NTIgODMuNzIxN0MxMTYuODcgODMuNzYyNyAxMTcuMDA5IDgzLjc4MzIgMTE3LjE2OSA4My43ODMyQzExNy4yODMgODMuNzgzMiAxMTcuMzkyIDgzLjc3NjQgMTE3LjQ5NyA4My43NjI3QzExNy42MDIgODMuNzQ5IDExNy42ODYgODMuNzM1NCAxMTcuNzUgODMuNzIxN0wxMTcuNzU3IDg0Ljk3OTVDMTE3LjYyIDg1LjAyMDUgMTE3LjQ2MSA4NS4wNTcgMTE3LjI3OCA4NS4wODg5QzExNy4xMDEgODUuMTIwOCAxMTYuODk2IDg1LjEzNjcgMTE2LjY2MyA4NS4xMzY3QzExNi4yODUgODUuMTM2NyAxMTUuOTUgODUuMDcwNiAxMTUuNjU4IDg0LjkzODVDMTE1LjM2NyA4NC44MDE4IDExNS4xMzkgODQuNTgwNyAxMTQuOTc1IDg0LjI3NTRDMTE0LjgxMSA4My45NzAxIDExNC43MjkgODMuNTY0NSAxMTQuNzI5IDgzLjA1ODZWNzUuNzkyWk0xMjMuNjY3IDgzLjI1NjhWNzcuNjAzNUgxMjUuMzIxVjg1SDEyMy43NjNMMTIzLjY2NyA4My4yNTY4Wk0xMjMuODk5IDgxLjcxODhMMTI0LjQ1MyA4MS43MDUxQzEyNC40NTMgODIuMjAxOCAxMjQuMzk4IDgyLjY1OTggMTI0LjI4OSA4My4wNzkxQzEyNC4xOCA4My40OTM4IDEyNC4wMTEgODMuODU2MSAxMjMuNzgzIDg0LjE2NkMxMjMuNTU1IDg0LjQ3MTQgMTIzLjI2NCA4NC43MTA2IDEyMi45MDggODQuODgzOEMxMjIuNTUzIDg1LjA1MjQgMTIyLjEyNyA4NS4xMzY3IDEyMS42MyA4NS4xMzY3QzEyMS4yNyA4NS4xMzY3IDEyMC45MzkgODUuMDg0MyAxMjAuNjM5IDg0Ljk3OTVDMTIwLjMzOCA4NC44NzQ3IDEyMC4wNzggODQuNzEyOSAxMTkuODU5IDg0LjQ5NDFDMTE5LjY0NSA4NC4yNzU0IDExOS40NzkgODMuOTkwNiAxMTkuMzYgODMuNjM5NkMxMTkuMjQyIDgzLjI4ODcgMTE5LjE4MyA4Mi44Njk1IDExOS4xODMgODIuMzgxOFY3Ny42MDM1SDEyMC44M1Y4Mi4zOTU1QzEyMC44MyA4Mi42NjQ0IDEyMC44NjIgODIuODkgMTIwLjkyNiA4My4wNzIzQzEyMC45OSA4My4yNSAxMjEuMDc2IDgzLjM5MzYgMTIxLjE4NiA4My41MDI5QzEyMS4yOTUgODMuNjEyMyAxMjEuNDIzIDgzLjY4OTggMTIxLjU2OCA4My43MzU0QzEyMS43MTQgODMuNzgwOSAxMjEuODY5IDgzLjgwMzcgMTIyLjAzMyA4My44MDM3QzEyMi41MDMgODMuODAzNyAxMjIuODcyIDgzLjcxMjYgMTIzLjE0MSA4My41MzAzQzEyMy40MTQgODMuMzQzNCAxMjMuNjA4IDgzLjA5MjggMTIzLjcyMiA4Mi43NzgzQzEyMy44NCA4Mi40NjM5IDEyMy44OTkgODIuMTEwNyAxMjMuODk5IDgxLjcxODhaTTEyOC45MTQgNzkuMDExN1Y4NUgxMjcuMjY3Vjc3LjYwMzVIMTI4LjgzOUwxMjguOTE0IDc5LjAxMTdaTTEzMS4xNzcgNzcuNTU1N0wxMzEuMTYzIDc5LjA4NjlDMTMxLjA2MyA3OS4wNjg3IDEzMC45NTMgNzkuMDU1IDEzMC44MzUgNzkuMDQ1OUMxMzAuNzIxIDc5LjAzNjggMTMwLjYwNyA3OS4wMzIyIDEzMC40OTMgNzkuMDMyMkMxMzAuMjExIDc5LjAzMjIgMTI5Ljk2MiA3OS4wNzMyIDEyOS43NDggNzkuMTU1M0MxMjkuNTM0IDc5LjIzMjcgMTI5LjM1NCA3OS4zNDY3IDEyOS4yMDggNzkuNDk3MUMxMjkuMDY3IDc5LjY0MjkgMTI4Ljk1NyA3OS44MjA2IDEyOC44OCA4MC4wMzAzQzEyOC44MDIgODAuMjM5OSAxMjguNzU3IDgwLjQ3NDYgMTI4Ljc0MyA4MC43MzQ0TDEyOC4zNjcgODAuNzYxN0MxMjguMzY3IDgwLjI5NjkgMTI4LjQxMyA3OS44NjYyIDEyOC41MDQgNzkuNDY5N0MxMjguNTk1IDc5LjA3MzIgMTI4LjczMiA3OC43MjQ2IDEyOC45MTQgNzguNDIzOEMxMjkuMTAxIDc4LjEyMyAxMjkuMzMzIDc3Ljg4ODMgMTI5LjYxMSA3Ny43MTk3QzEyOS44OTQgNzcuNTUxMSAxMzAuMjIgNzcuNDY2OCAxMzAuNTg5IDc3LjQ2NjhDMTMwLjY4OSA3Ny40NjY4IDEzMC43OTYgNzcuNDc1OSAxMzAuOTEgNzcuNDk0MUMxMzEuMDI5IDc3LjUxMjQgMTMxLjExOCA3Ny41MzI5IDEzMS4xNzcgNzcuNTU1N1pNMTM1LjU4MyA4NS4xMzY3QzEzNS4wMzYgODUuMTM2NyAxMzQuNTQyIDg1LjA0NzkgMTM0LjEgODQuODcwMUMxMzMuNjYyIDg0LjY4NzggMTMzLjI4OCA4NC40MzQ5IDEzMi45NzkgODQuMTExM0MxMzIuNjczIDgzLjc4NzggMTMyLjQzOCA4My40MDcyIDEzMi4yNzQgODIuOTY5N0MxMzIuMTEgODIuNTMyMiAxMzIuMDI4IDgyLjA2MDUgMTMyLjAyOCA4MS41NTQ3VjgxLjI4MTJDMTMyLjAyOCA4MC43MDI1IDEzMi4xMTMgODAuMTc4NCAxMzIuMjgxIDc5LjcwOUMxMzIuNDUgNzkuMjM5NiAxMzIuNjg1IDc4LjgzODUgMTMyLjk4NSA3OC41MDU5QzEzMy4yODYgNzguMTY4NiAxMzMuNjQyIDc3LjkxMTEgMTM0LjA1MiA3Ny43MzM0QzEzNC40NjIgNzcuNTU1NyAxMzQuOTA2IDc3LjQ2NjggMTM1LjM4NSA3Ny40NjY4QzEzNS45MTMgNzcuNDY2OCAxMzYuMzc2IDc3LjU1NTcgMTM2Ljc3MiA3Ny43MzM0QzEzNy4xNjkgNzcuOTExMSAxMzcuNDk3IDc4LjE2MTggMTM3Ljc1NyA3OC40ODU0QzEzOC4wMjEgNzguODA0NCAxMzguMjE3IDc5LjE4NDkgMTM4LjM0NSA3OS42MjdDMTM4LjQ3NyA4MC4wNjkgMTM4LjU0MyA4MC41NTY2IDEzOC41NDMgODEuMDg5OFY4MS43OTM5SDEzMi44MjhWODAuNjExM0gxMzYuOTE2VjgwLjQ4MTRDMTM2LjkwNyA4MC4xODUyIDEzNi44NDggNzkuOTA3MiAxMzYuNzM4IDc5LjY0NzVDMTM2LjYzMyA3OS4zODc3IDEzNi40NzIgNzkuMTc4MSAxMzYuMjUzIDc5LjAxODZDMTM2LjAzNCA3OC44NTkgMTM1Ljc0MyA3OC43NzkzIDEzNS4zNzggNzguNzc5M0MxMzUuMTA0IDc4Ljc3OTMgMTM0Ljg2MSA3OC44Mzg1IDEzNC42NDYgNzguOTU3QzEzNC40MzcgNzkuMDcxIDEzNC4yNjEgNzkuMjM3MyAxMzQuMTIgNzkuNDU2MUMxMzMuOTc5IDc5LjY3NDggMTMzLjg2OSA3OS45MzkxIDEzMy43OTIgODAuMjQ5QzEzMy43MTkgODAuNTU0NCAxMzMuNjgzIDgwLjg5ODQgMTMzLjY4MyA4MS4yODEyVjgxLjU1NDdDMTMzLjY4MyA4MS44NzgzIDEzMy43MjYgODIuMTc5IDEzMy44MTIgODIuNDU3QzEzMy45MDQgODIuNzMwNSAxMzQuMDM2IDgyLjk2OTcgMTM0LjIwOSA4My4xNzQ4QzEzNC4zODIgODMuMzc5OSAxMzQuNTkyIDgzLjU0MTcgMTM0LjgzOCA4My42NjAyQzEzNS4wODQgODMuNzc0MSAxMzUuMzY0IDgzLjgzMTEgMTM1LjY3OSA4My44MzExQzEzNi4wNzUgODMuODMxMSAxMzYuNDI4IDgzLjc1MTMgMTM2LjczOCA4My41OTE4QzEzNy4wNDggODMuNDMyMyAxMzcuMzE3IDgzLjIwNjcgMTM3LjU0NSA4Mi45MTVMMTM4LjQxMyA4My43NTU5QzEzOC4yNTQgODMuOTg4MyAxMzguMDQ2IDg0LjIxMTYgMTM3Ljc5MSA4NC40MjU4QzEzNy41MzYgODQuNjM1NCAxMzcuMjI0IDg0LjgwNjMgMTM2Ljg1NCA4NC45Mzg1QzEzNi40OSA4NS4wNzA2IDEzNi4wNjYgODUuMTM2NyAxMzUuNTgzIDg1LjEzNjdaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0xNjIuOTI0IDgzLjY4NzVWODVIMTU2LjI1MlY4My44NzIxTDE1OS40OTIgODAuMzM3OUMxNTkuODQ4IDc5LjkzNjggMTYwLjEyOCA3OS41OTA1IDE2MC4zMzMgNzkuMjk4OEMxNjAuNTM4IDc5LjAwNzIgMTYwLjY4MiA3OC43NDUxIDE2MC43NjQgNzguNTEyN0MxNjAuODUgNzguMjc1NyAxNjAuODk0IDc4LjA0NTYgMTYwLjg5NCA3Ny44MjIzQzE2MC44OTQgNzcuNTA3OCAxNjAuODM0IDc3LjIzMjEgMTYwLjcxNiA3Ni45OTUxQzE2MC42MDIgNzYuNzUzNiAxNjAuNDMzIDc2LjU2NDUgMTYwLjIxIDc2LjQyNzdDMTU5Ljk4NyA3Ni4yODY1IDE1OS43MTUgNzYuMjE1OCAxNTkuMzk2IDc2LjIxNThDMTU5LjAyNyA3Ni4yMTU4IDE1OC43MTcgNzYuMjk1NiAxNTguNDY3IDc2LjQ1NTFDMTU4LjIxNiA3Ni42MTQ2IDE1OC4wMjcgNzYuODM1NiAxNTcuODk5IDc3LjExODJDMTU3Ljc3MiA3Ny4zOTYyIDE1Ny43MDggNzcuNzE1MiAxNTcuNzA4IDc4LjA3NTJIMTU2LjA2MUMxNTYuMDYxIDc3LjQ5NjQgMTU2LjE5MyA3Ni45Njc4IDE1Ni40NTcgNzYuNDg5M0MxNTYuNzIxIDc2LjAwNjIgMTU3LjEwNCA3NS42MjM0IDE1Ny42MDUgNzUuMzQwOEMxNTguMTA3IDc1LjA1MzcgMTU4LjcxMSA3NC45MTAyIDE1OS40MTcgNzQuOTEwMkMxNjAuMDgyIDc0LjkxMDIgMTYwLjY0NyA3NS4wMjE4IDE2MS4xMTIgNzUuMjQ1MUMxNjEuNTc3IDc1LjQ2ODQgMTYxLjkzIDc1Ljc4NTIgMTYyLjE3MiA3Ni4xOTUzQzE2Mi40MTggNzYuNjA1NSAxNjIuNTQxIDc3LjA5MDggMTYyLjU0MSA3Ny42NTE0QzE2Mi41NDEgNzcuOTYxMyAxNjIuNDkxIDc4LjI2ODkgMTYyLjM5MSA3OC41NzQyQzE2Mi4yOSA3OC44Nzk2IDE2Mi4xNDcgNzkuMTg0OSAxNjEuOTYgNzkuNDkwMkMxNjEuNzc4IDc5Ljc5MSAxNjEuNTYxIDgwLjA5NDEgMTYxLjMxMSA4MC4zOTk0QzE2MS4wNiA4MC43MDAyIDE2MC43ODQgODEuMDA1NSAxNjAuNDgzIDgxLjMxNTRMMTU4LjMzIDgzLjY4NzVIMTYyLjkyNFpNMTcxLjEzMSA4My42ODc1Vjg1SDE2NC40NTlWODMuODcyMUwxNjcuNjk5IDgwLjMzNzlDMTY4LjA1NSA3OS45MzY4IDE2OC4zMzUgNzkuNTkwNSAxNjguNTQgNzkuMjk4OEMxNjguNzQ1IDc5LjAwNzIgMTY4Ljg4OSA3OC43NDUxIDE2OC45NzEgNzguNTEyN0MxNjkuMDU3IDc4LjI3NTcgMTY5LjEwMSA3OC4wNDU2IDE2OS4xMDEgNzcuODIyM0MxNjkuMTAxIDc3LjUwNzggMTY5LjA0MSA3Ny4yMzIxIDE2OC45MjMgNzYuOTk1MUMxNjguODA5IDc2Ljc1MzYgMTY4LjY0IDc2LjU2NDUgMTY4LjQxNyA3Ni40Mjc3QzE2OC4xOTQgNzYuMjg2NSAxNjcuOTIzIDc2LjIxNTggMTY3LjYwNCA3Ni4yMTU4QzE2Ny4yMzQgNzYuMjE1OCAxNjYuOTI0IDc2LjI5NTYgMTY2LjY3NCA3Ni40NTUxQzE2Ni40MjMgNzYuNjE0NiAxNjYuMjM0IDc2LjgzNTYgMTY2LjEwNiA3Ny4xMTgyQzE2NS45NzkgNzcuMzk2MiAxNjUuOTE1IDc3LjcxNTIgMTY1LjkxNSA3OC4wNzUySDE2NC4yNjhDMTY0LjI2OCA3Ny40OTY0IDE2NC40IDc2Ljk2NzggMTY0LjY2NCA3Ni40ODkzQzE2NC45MjggNzYuMDA2MiAxNjUuMzExIDc1LjYyMzQgMTY1LjgxMiA3NS4zNDA4QzE2Ni4zMTQgNzUuMDUzNyAxNjYuOTE4IDc0LjkxMDIgMTY3LjYyNCA3NC45MTAyQzE2OC4yODkgNzQuOTEwMiAxNjguODU0IDc1LjAyMTggMTY5LjMxOSA3NS4yNDUxQzE2OS43ODQgNzUuNDY4NCAxNzAuMTM3IDc1Ljc4NTIgMTcwLjM3OSA3Ni4xOTUzQzE3MC42MjUgNzYuNjA1NSAxNzAuNzQ4IDc3LjA5MDggMTcwLjc0OCA3Ny42NTE0QzE3MC43NDggNzcuOTYxMyAxNzAuNjk4IDc4LjI2ODkgMTcwLjU5OCA3OC41NzQyQzE3MC40OTcgNzguODc5NiAxNzAuMzU0IDc5LjE4NDkgMTcwLjE2NyA3OS40OTAyQzE2OS45ODUgNzkuNzkxIDE2OS43NjggODAuMDk0MSAxNjkuNTE4IDgwLjM5OTRDMTY5LjI2NyA4MC43MDAyIDE2OC45OTEgODEuMDA1NSAxNjguNjkgODEuMzE1NEwxNjYuNTM3IDgzLjY4NzVIMTcxLjEzMVpNMTcyLjc5NiA3Ni43MzU0QzE3Mi43OTYgNzYuNDAyNyAxNzIuODc4IDc2LjA5NzMgMTczLjA0MiA3NS44MTkzQzE3My4yMDYgNzUuNTQxMyAxNzMuNDI1IDc1LjMyMDMgMTczLjY5OCA3NS4xNTYyQzE3My45NzYgNzQuOTg3NiAxNzQuMjc3IDc0LjkwMzMgMTc0LjYwMSA3NC45MDMzQzE3NC45MjkgNzQuOTAzMyAxNzUuMjI3IDc0Ljk4NzYgMTc1LjQ5NiA3NS4xNTYyQzE3NS43NjUgNzUuMzIwMyAxNzUuOTc5IDc1LjU0MTMgMTc2LjEzOSA3NS44MTkzQzE3Ni4zMDMgNzYuMDk3MyAxNzYuMzg1IDc2LjQwMjcgMTc2LjM4NSA3Ni43MzU0QzE3Ni4zODUgNzcuMDY4IDE3Ni4zMDMgNzcuMzczNCAxNzYuMTM5IDc3LjY1MTRDMTc1Ljk3OSA3Ny45MjQ4IDE3NS43NjUgNzguMTQxMyAxNzUuNDk2IDc4LjMwMDhDMTc1LjIyNyA3OC40NjAzIDE3NC45MjkgNzguNTQgMTc0LjYwMSA3OC41NEMxNzQuMjc3IDc4LjU0IDE3My45NzYgNzguNDYwMyAxNzMuNjk4IDc4LjMwMDhDMTczLjQyNSA3OC4xNDEzIDE3My4yMDYgNzcuOTI0OCAxNzMuMDQyIDc3LjY1MTRDMTcyLjg3OCA3Ny4zNzM0IDE3Mi43OTYgNzcuMDY4IDE3Mi43OTYgNzYuNzM1NFpNMTczLjcxOSA3Ni43MzU0QzE3My43MTkgNzYuOTgxNCAxNzMuODA1IDc3LjE4ODggMTczLjk3OSA3Ny4zNTc0QzE3NC4xNTIgNzcuNTIxNSAxNzQuMzU5IDc3LjYwMzUgMTc0LjYwMSA3Ny42MDM1QzE3NC44NDIgNzcuNjAzNSAxNzUuMDQ1IDc3LjUyMTUgMTc1LjIwOSA3Ny4zNTc0QzE3NS4zNzMgNzcuMTkzNCAxNzUuNDU1IDc2Ljk4NiAxNzUuNDU1IDc2LjczNTRDMTc1LjQ1NSA3Ni40ODAxIDE3NS4zNzMgNzYuMjY4MiAxNzUuMjA5IDc2LjA5OTZDMTc1LjA0NSA3NS45MzEgMTc0Ljg0MiA3NS44NDY3IDE3NC42MDEgNzUuODQ2N0MxNzQuMzU5IDc1Ljg0NjcgMTc0LjE1MiA3NS45MzEgMTczLjk3OSA3Ni4wOTk2QzE3My44MDUgNzYuMjY4MiAxNzMuNzE5IDc2LjQ4MDEgMTczLjcxOSA3Ni43MzU0Wk0xODQuNDE0IDgxLjc1OThIMTg2LjEyM0MxODYuMDY4IDgyLjQxMTUgMTg1Ljg4NiA4Mi45OTI1IDE4NS41NzYgODMuNTAyOUMxODUuMjY2IDg0LjAwODggMTg0LjgzMSA4NC40MDc2IDE4NC4yNzEgODQuNjk5MkMxODMuNzEgODQuOTkwOSAxODMuMDI5IDg1LjEzNjcgMTgyLjIyNyA4NS4xMzY3QzE4MS42MTEgODUuMTM2NyAxODEuMDU4IDg1LjAyNzMgMTgwLjU2NSA4NC44MDg2QzE4MC4wNzMgODQuNTg1MyAxNzkuNjUyIDg0LjI3MDggMTc5LjMwMSA4My44NjUyQzE3OC45NSA4My40NTUxIDE3OC42ODEgODIuOTYwNiAxNzguNDk0IDgyLjM4MThDMTc4LjMxMiA4MS44MDMxIDE3OC4yMjEgODEuMTU1OSAxNzguMjIxIDgwLjQ0MDRWNzkuNjEzM0MxNzguMjIxIDc4Ljg5NzggMTc4LjMxNCA3OC4yNTA3IDE3OC41MDEgNzcuNjcxOUMxNzguNjkyIDc3LjA5MzEgMTc4Ljk2NiA3Ni41OTg2IDE3OS4zMjEgNzYuMTg4NUMxNzkuNjc3IDc1Ljc3MzggMTgwLjEwMyA3NS40NTcgMTgwLjYgNzUuMjM4M0MxODEuMTAxIDc1LjAxOTUgMTgxLjY2NCA3NC45MTAyIDE4Mi4yODggNzQuOTEwMkMxODMuMDgxIDc0LjkxMDIgMTgzLjc1MSA3NS4wNTYgMTg0LjI5OCA3NS4zNDc3QzE4NC44NDUgNzUuNjM5MyAxODUuMjY5IDc2LjA0MjYgMTg1LjU2OSA3Ni41NTc2QzE4NS44NzUgNzcuMDcyNiAxODYuMDYyIDc3LjY2MjggMTg2LjEzIDc4LjMyODFIMTg0LjQyMUMxODQuMzc1IDc3Ljg5OTcgMTg0LjI3NSA3Ny41MzI5IDE4NC4xMiA3Ny4yMjc1QzE4My45NyA3Ni45MjIyIDE4My43NDYgNzYuNjg5OCAxODMuNDUgNzYuNTMwM0MxODMuMTU0IDc2LjM2NjIgMTgyLjc2NyA3Ni4yODQyIDE4Mi4yODggNzYuMjg0MkMxODEuODk2IDc2LjI4NDIgMTgxLjU1NCA3Ni4zNTcxIDE4MS4yNjMgNzYuNTAyOUMxODAuOTcxIDc2LjY0ODggMTgwLjcyNyA3Ni44NjMgMTgwLjUzMSA3Ny4xNDU1QzE4MC4zMzUgNzcuNDI4MSAxODAuMTg3IDc3Ljc3NjcgMTgwLjA4NyA3OC4xOTE0QzE3OS45OTEgNzguNjAxNiAxNzkuOTQzIDc5LjA3MSAxNzkuOTQzIDc5LjU5OTZWODAuNDQwNEMxNzkuOTQzIDgwLjk0MTcgMTc5Ljk4NyA4MS4zOTc1IDE4MC4wNzMgODEuODA3NkMxODAuMTY0IDgyLjIxMzIgMTgwLjMwMSA4Mi41NjE4IDE4MC40ODMgODIuODUzNUMxODAuNjcgODMuMTQ1MiAxODAuOTA3IDgzLjM3MDggMTgxLjE5NCA4My41MzAzQzE4MS40ODEgODMuNjg5OCAxODEuODI2IDgzLjc2OTUgMTgyLjIyNyA4My43Njk1QzE4Mi43MTQgODMuNzY5NSAxODMuMTA4IDgzLjY5MjEgMTgzLjQwOSA4My41MzcxQzE4My43MTUgODMuMzgyMiAxODMuOTQ1IDgzLjE1NjYgMTg0LjEgODIuODYwNEMxODQuMjU5IDgyLjU1OTYgMTg0LjM2NCA4Mi4xOTI3IDE4NC40MTQgODEuNzU5OFoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPC9nPgo8ZGVmcz4KPGZpbHRlciBpZD0iZmlsdGVyMF9kXzQ5ODhfNDUwNTYiIHg9IjAiIHk9IjQ4IiB3aWR0aD0iMjEyIiBoZWlnaHQ9IjY4IiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+CjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+CjxmZUNvbG9yTWF0cml4IGluPSJTb3VyY2VBbHBoYSIgdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDEyNyAwIiByZXN1bHQ9ImhhcmRBbHBoYSIvPgo8ZmVPZmZzZXQgZHk9IjIiLz4KPGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iMyIvPgo8ZmVDb2xvck1hdHJpeCB0eXBlPSJtYXRyaXgiIHZhbHVlcz0iMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMC4xMiAwIi8+CjxmZUJsZW5kIG1vZGU9Im5vcm1hbCIgaW4yPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHJlc3VsdD0iZWZmZWN0MV9kcm9wU2hhZG93XzQ5ODhfNDUwNTYiLz4KPGZlQmxlbmQgbW9kZT0ibm9ybWFsIiBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2Ryb3BTaGFkb3dfNDk4OF80NTA1NiIgcmVzdWx0PSJzaGFwZSIvPgo8L2ZpbHRlcj4KPGZpbHRlciBpZD0iZmlsdGVyMV9iXzQ5ODhfNDUwNTYiIHg9IjEyIiB5PSI1OCIgd2lkdGg9IjE4OCIgaGVpZ2h0PSI0NCIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiPgo8ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPgo8ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIzIi8+CjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl80OTg4XzQ1MDU2Ii8+CjxmZUJsZW5kIG1vZGU9Im5vcm1hbCIgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl80OTg4XzQ1MDU2IiByZXN1bHQ9InNoYXBlIi8+CjwvZmlsdGVyPgo8L2RlZnM+Cjwvc3ZnPgo=", "description": "Displays a single entity attribute or the latest telemetry with optional label and icon.", "descriptor": { "type": "latest", @@ -14,10 +14,12 @@ "controllerScript": "self.onInit = function() {\n self.ctx.$scope.labelValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.labelValueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '280px',\n previewHeight: '80px',\n embedTitlePanel: true\n };\n};\n\nself.actionSources = function() {\n return {\n 'cardClick': {\n name: 'widget-action.card-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n};\n", "settingsSchema": "", "dataKeySettingsSchema": "", - "settingsDirective": "", - "hasBasicMode": false, - "basicModeDirective": "", + "settingsDirective": "tb-label-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-label-value-card-basic-config", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{},\"title\":\"Label & value card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" }, - "tags": null + "tags": [ + "label" + ] } \ No newline at end of file diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts index 1a705c3c6f..ac8cb25d60 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts @@ -136,6 +136,9 @@ import { MobileAppQrCodeBasicConfigComponent } from '@home/components/widget/con import { LabelCardBasicConfigComponent } from '@home/components/widget/config/basic/cards/label-card-basic-config.component'; +import { + LabelValueCardBasicConfigComponent +} from '@home/components/widget/config/basic/cards/label-value-card-basic-config.component'; @NgModule({ declarations: [ @@ -181,7 +184,8 @@ import { RadarChartBasicConfigComponent, DigitalSimpleGaugeBasicConfigComponent, MobileAppQrCodeBasicConfigComponent, - LabelCardBasicConfigComponent + LabelCardBasicConfigComponent, + LabelValueCardBasicConfigComponent ], imports: [ CommonModule, @@ -229,7 +233,8 @@ import { RadarChartBasicConfigComponent, DigitalSimpleGaugeBasicConfigComponent, MobileAppQrCodeBasicConfigComponent, - LabelCardBasicConfigComponent + LabelCardBasicConfigComponent, + LabelValueCardBasicConfigComponent ] }) export class BasicWidgetConfigModule { @@ -271,5 +276,6 @@ export const basicWidgetConfigComponentsMap: {[key: string]: Type + + + + + +
+
widget-config.appearance
+
+ + {{ 'widgets.label-card.auto-scale' | translate }} + +
+
+ + {{ 'widgets.label-card.label' | translate }} + +
+ + + + + + + +
+
+
+ + {{ 'widgets.label-card.icon' | translate }} + +
+ + + + + + + + +
+
+
+
widgets.label-value-card.value
+
+ + + +
widget-config.decimals-suffix
+
+ + + + +
+
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
widget-config.show-card-buttons
+ + {{ 'fullscreen.fullscreen' | translate }} + +
+
+
{{ 'widget-config.card-border-radius' | translate }}
+ + + +
+
+
{{ 'widget-config.card-padding' | translate }}
+ + + +
+
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/label-value-card-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/label-value-card-basic-config.component.ts new file mode 100644 index 0000000000..5add2284ab --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/label-value-card-basic-config.component.ts @@ -0,0 +1,191 @@ +/// +/// Copyright © 2016-2024 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; +import { + DataKey, + datasourcesHasAggregation, + datasourcesHasOnlyComparisonAggregation, + WidgetConfig, +} from '@shared/models/widget.models'; +import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; +import { formatValue, isUndefined } from '@core/utils'; +import { + labelValueCardWidgetDefaultSettings, + LabelValueCardWidgetSettings +} from '@home/components/widget/lib/cards/label-value-card-widget.models'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { + getTimewindowConfig, + setTimewindowConfig +} from '@home/components/widget/config/timewindow-config-panel.component'; + +@Component({ + selector: 'tb-label-value-card-basic-config', + templateUrl: './label-value-card-basic-config.component.html', + styleUrls: ['../basic-config.scss'] +}) +export class LabelValueCardBasicConfigComponent extends BasicWidgetConfigComponent { + + public get displayTimewindowConfig(): boolean { + const datasources = this.labelValueCardWidgetConfigForm.get('datasources').value; + return datasourcesHasAggregation(datasources); + } + + public onlyHistoryTimewindow(): boolean { + const datasources = this.labelValueCardWidgetConfigForm.get('datasources').value; + return datasourcesHasOnlyComparisonAggregation(datasources); + } + + labelValueCardWidgetConfigForm: UntypedFormGroup; + + valuePreviewFn = this._valuePreviewFn.bind(this); + + constructor(protected store: Store, + protected widgetConfigComponent: WidgetConfigComponent, + private fb: UntypedFormBuilder) { + super(store, widgetConfigComponent); + } + + protected configForm(): UntypedFormGroup { + return this.labelValueCardWidgetConfigForm; + } + + protected defaultDataKeys(configData: WidgetConfigComponentData): DataKey[] { + return [{ name: 'temperature', label: 'Temperature', type: DataKeyType.timeseries }]; + } + + protected onConfigSet(configData: WidgetConfigComponentData) { + const settings: LabelValueCardWidgetSettings = {...labelValueCardWidgetDefaultSettings, ...(configData.config.settings || {})}; + this.labelValueCardWidgetConfigForm = this.fb.group({ + timewindowConfig: [getTimewindowConfig(configData.config), []], + datasources: [configData.config.datasources, []], + autoScale: [settings.autoScale, []], + + showLabel: [settings.showLabel, []], + label: [settings.label, []], + labelFont: [settings.labelFont, []], + labelColor: [settings.labelColor, []], + + showIcon: [settings.showIcon, []], + iconSize: [settings.iconSize, [Validators.min(0)]], + iconSizeUnit: [settings.iconSizeUnit, []], + icon: [settings.icon, []], + iconColor: [settings.iconColor, []], + + units: [configData.config.units, []], + decimals: [configData.config.decimals, []], + valueFont: [settings.valueFont, []], + valueColor: [settings.valueColor, []], + + background: [settings.background, []], + + cardButtons: [this.getCardButtons(configData.config), []], + borderRadius: [configData.config.borderRadius, []], + padding: [settings.padding, []], + + actions: [configData.config.actions || {}, []] + }); + } + + protected prepareOutputConfig(config: any): WidgetConfigComponentData { + setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig); + this.widgetConfig.config.datasources = config.datasources; + + this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; + + this.widgetConfig.config.settings.autoScale = config.autoScale; + + this.widgetConfig.config.settings.showLabel = config.showLabel; + this.widgetConfig.config.settings.label = config.label; + this.widgetConfig.config.settings.labelFont = config.labelFont; + this.widgetConfig.config.settings.labelColor = config.labelColor; + + this.widgetConfig.config.settings.showIcon = config.showIcon; + this.widgetConfig.config.settings.iconSize = config.iconSize; + this.widgetConfig.config.settings.iconSizeUnit = config.iconSizeUnit; + this.widgetConfig.config.settings.icon = config.icon; + this.widgetConfig.config.settings.iconColor = config.iconColor; + + this.widgetConfig.config.units = config.units; + this.widgetConfig.config.decimals = config.decimals; + this.widgetConfig.config.settings.valueFont = config.valueFont; + this.widgetConfig.config.settings.valueColor = config.valueColor; + + this.widgetConfig.config.settings.background = config.background; + + this.setCardButtons(config.cardButtons, this.widgetConfig.config); + this.widgetConfig.config.borderRadius = config.borderRadius; + this.widgetConfig.config.settings.padding = config.padding; + + this.widgetConfig.config.actions = config.actions; + return this.widgetConfig; + } + + protected validatorTriggers(): string[] { + return ['showLabel', 'showIcon']; + } + + protected updateValidators(emitEvent: boolean, trigger?: string) { + const showLabel: boolean = this.labelValueCardWidgetConfigForm.get('showLabel').value; + const showIcon: boolean = this.labelValueCardWidgetConfigForm.get('showIcon').value; + + if (showLabel) { + this.labelValueCardWidgetConfigForm.get('label').enable(); + this.labelValueCardWidgetConfigForm.get('labelFont').enable(); + this.labelValueCardWidgetConfigForm.get('labelColor').enable(); + } else { + this.labelValueCardWidgetConfigForm.get('label').disable(); + this.labelValueCardWidgetConfigForm.get('labelFont').disable(); + this.labelValueCardWidgetConfigForm.get('labelColor').disable(); + } + + if (showIcon) { + this.labelValueCardWidgetConfigForm.get('iconSize').enable(); + this.labelValueCardWidgetConfigForm.get('iconSizeUnit').enable(); + this.labelValueCardWidgetConfigForm.get('icon').enable(); + this.labelValueCardWidgetConfigForm.get('iconColor').enable(); + } else { + this.labelValueCardWidgetConfigForm.get('iconSize').disable(); + this.labelValueCardWidgetConfigForm.get('iconSizeUnit').disable(); + this.labelValueCardWidgetConfigForm.get('icon').disable(); + this.labelValueCardWidgetConfigForm.get('iconColor').disable(); + } + } + + private getCardButtons(config: WidgetConfig): string[] { + const buttons: string[] = []; + if (isUndefined(config.enableFullscreen) || config.enableFullscreen) { + buttons.push('fullscreen'); + } + return buttons; + } + + private setCardButtons(buttons: string[], config: WidgetConfig) { + config.enableFullscreen = buttons.includes('fullscreen'); + } + + private _valuePreviewFn(): string { + const units: string = this.labelValueCardWidgetConfigForm.get('units').value; + const decimals: number = this.labelValueCardWidgetConfigForm.get('decimals').value; + return formatValue(22, decimals, units, true); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/label-value-card-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/label-value-card-widget.component.ts index c4aecd7943..9a47a5db9d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/label-value-card-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/label-value-card-widget.component.ts @@ -165,10 +165,8 @@ export class LabelValueCardWidgetComponent implements OnInit, AfterViewInit, OnD public onDataUpdated() { const tsValue = getSingleTsValue(this.ctx.data); - let ts; let value; if (tsValue && isDefinedAndNotNull(tsValue[1]) && tsValue[0] !== 0) { - ts = tsValue[0]; value = tsValue[1]; this.valueText = formatValue(value, this.decimals, this.units, false); } else { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-card-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-card-widget-settings.component.ts index b3ebca2ae8..166170c8ee 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-card-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-card-widget-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, Injector } from '@angular/core'; +import { Component } from '@angular/core'; import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; @@ -31,7 +31,6 @@ export class LabelCardWidgetSettingsComponent extends WidgetSettingsComponent { labelCardWidgetSettingsForm: UntypedFormGroup; constructor(protected store: Store, - private $injector: Injector, private fb: UntypedFormBuilder) { super(store); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-value-card-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-value-card-widget-settings.component.html new file mode 100644 index 0000000000..b10d497f6c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-value-card-widget-settings.component.html @@ -0,0 +1,81 @@ + + +
+
widgets.label-value-card.label-value-card-style
+
+ + {{ 'widgets.label-card.auto-scale' | translate }} + +
+
+ + {{ 'widgets.label-card.label' | translate }} + +
+ + + + + + + +
+
+
+ + {{ 'widgets.label-card.icon' | translate }} + +
+ + + + + + + + +
+
+
+
widgets.label-value-card.value
+
+ + + + +
+
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
{{ 'widget-config.card-padding' | translate }}
+ + + +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-value-card-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-value-card-widget-settings.component.ts new file mode 100644 index 0000000000..b8c80ba0de --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-value-card-widget-settings.component.ts @@ -0,0 +1,108 @@ +/// +/// Copyright © 2016-2024 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { labelValueCardWidgetDefaultSettings } from '@home/components/widget/lib/cards/label-value-card-widget.models'; +import { formatValue } from '@core/utils'; + +@Component({ + selector: 'tb-label-value-card-widget-settings', + templateUrl: './label-value-card-widget-settings.component.html', + styleUrls: [] +}) +export class LabelValueCardWidgetSettingsComponent extends WidgetSettingsComponent { + + labelValueCardWidgetSettingsForm: UntypedFormGroup; + + valuePreviewFn = this._valuePreviewFn.bind(this); + + constructor(protected store: Store, + private fb: UntypedFormBuilder) { + super(store); + } + + protected settingsForm(): UntypedFormGroup { + return this.labelValueCardWidgetSettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return {...labelValueCardWidgetDefaultSettings}; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.labelValueCardWidgetSettingsForm = this.fb.group({ + autoScale: [settings.autoScale, []], + + showLabel: [settings.showLabel, []], + label: [settings.label, []], + labelFont: [settings.labelFont, []], + labelColor: [settings.labelColor, []], + + showIcon: [settings.showIcon, []], + iconSize: [settings.iconSize, [Validators.min(0)]], + iconSizeUnit: [settings.iconSizeUnit, []], + icon: [settings.icon, []], + iconColor: [settings.iconColor, []], + + valueFont: [settings.valueFont, []], + valueColor: [settings.valueColor, []], + + background: [settings.background, []], + padding: [settings.padding, []] + }); + } + + protected validatorTriggers(): string[] { + return ['showLabel', 'showIcon']; + } + + protected updateValidators(emitEvent: boolean) { + const showLabel: boolean = this.labelValueCardWidgetSettingsForm.get('showLabel').value; + const showIcon: boolean = this.labelValueCardWidgetSettingsForm.get('showIcon').value; + + if (showLabel) { + this.labelValueCardWidgetSettingsForm.get('label').enable(); + this.labelValueCardWidgetSettingsForm.get('labelFont').enable(); + this.labelValueCardWidgetSettingsForm.get('labelColor').enable(); + } else { + this.labelValueCardWidgetSettingsForm.get('label').disable(); + this.labelValueCardWidgetSettingsForm.get('labelFont').disable(); + this.labelValueCardWidgetSettingsForm.get('labelColor').disable(); + } + + if (showIcon) { + this.labelValueCardWidgetSettingsForm.get('iconSize').enable(); + this.labelValueCardWidgetSettingsForm.get('iconSizeUnit').enable(); + this.labelValueCardWidgetSettingsForm.get('icon').enable(); + this.labelValueCardWidgetSettingsForm.get('iconColor').enable(); + } else { + this.labelValueCardWidgetSettingsForm.get('iconSize').disable(); + this.labelValueCardWidgetSettingsForm.get('iconSizeUnit').disable(); + this.labelValueCardWidgetSettingsForm.get('icon').disable(); + this.labelValueCardWidgetSettingsForm.get('iconColor').disable(); + } + } + + private _valuePreviewFn(): string { + const units: string = this.widgetConfig.config.units; + const decimals: number = this.widgetConfig.config.decimals; + return formatValue(22, decimals, units, true); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts index 47db116fbd..e4df67e2f8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts @@ -359,6 +359,9 @@ import { import { LabelCardWidgetSettingsComponent } from '@home/components/widget/lib/settings/cards/label-card-widget-settings.component'; +import { + LabelValueCardWidgetSettingsComponent +} from '@home/components/widget/lib/settings/cards/label-value-card-widget-settings.component'; @NgModule({ declarations: [ @@ -486,7 +489,8 @@ import { BarChartWidgetSettingsComponent, PolarAreaChartWidgetSettingsComponent, RadarChartWidgetSettingsComponent, - LabelCardWidgetSettingsComponent + LabelCardWidgetSettingsComponent, + LabelValueCardWidgetSettingsComponent ], imports: [ CommonModule, @@ -619,7 +623,8 @@ import { BarChartWidgetSettingsComponent, PolarAreaChartWidgetSettingsComponent, RadarChartWidgetSettingsComponent, - LabelCardWidgetSettingsComponent + LabelCardWidgetSettingsComponent, + LabelValueCardWidgetSettingsComponent ] }) export class WidgetSettingsModule { @@ -719,5 +724,6 @@ export const widgetSettingsComponentsMap: {[key: string]: Type