@opengis/fastify-table 1.3.21 → 1.3.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/index.js +17 -6
  2. package/package.json +6 -3
  3. package/server/helpers/format/formatAuto.js +13 -0
  4. package/server/helpers/format/formatDate.js +258 -0
  5. package/server/helpers/format/formatDigit.js +21 -0
  6. package/server/helpers/format/formatNum.js +361 -0
  7. package/server/helpers/format/formatNumber.js +54 -0
  8. package/server/helpers/format/formatRelative.js +106 -0
  9. package/server/helpers/format/formatUnit.js +38 -0
  10. package/server/helpers/format/num_format.js +42 -0
  11. package/server/helpers/format/set.js +26 -0
  12. package/server/helpers/funcs/_math.js +50 -0
  13. package/server/helpers/funcs/contentList.js +58 -0
  14. package/server/helpers/funcs/empty.js +21 -0
  15. package/server/helpers/funcs/ifCond.js +106 -0
  16. package/server/helpers/funcs/ifCondAnd.js +96 -0
  17. package/server/helpers/funcs/ifCondOr.js +98 -0
  18. package/server/helpers/funcs/inc.js +21 -0
  19. package/server/helpers/funcs/json.js +3 -0
  20. package/server/helpers/funcs/qrcode.js +66 -0
  21. package/server/helpers/funcs/round.js +28 -0
  22. package/server/helpers/funcs/select.js +50 -0
  23. package/server/helpers/index.js +70 -3
  24. package/server/helpers/string/coalesce.js +31 -0
  25. package/server/helpers/string/concat.js +28 -0
  26. package/server/helpers/string/split.js +20 -0
  27. package/server/helpers/string/str_replace.js +61 -0
  28. package/server/helpers/string/substr.js +32 -0
  29. package/server/helpers/string/translit.js +23 -0
  30. package/server/helpers/string/utils/alphabet.js +76 -0
  31. package/server/plugins/crud/funcs/utils/logChanges.js +1 -1
  32. package/server/plugins/pg/funcs/getMeta.js +1 -1
  33. package/server/plugins/table/funcs/metaFormat/index.js +1 -1
  34. package/utils.js +2 -2
@@ -0,0 +1,58 @@
1
+ import Handlebars from 'handlebars';
2
+
3
+ import getPG from '../../plugins/pg/funcs/getPG.js';
4
+
5
+ const maxLimit = 100;
6
+
7
+ /**
8
+ * Відображення даних з таблиці або запиту до БД на сторінці. За запитом отримуємо масив json із рядків бази даних.
9
+ * Є можливість застосування синтаксису sql у змінній query для формування запиту до БД.
10
+ *
11
+ * @summary Відображення контенту на сторінці. Є можливість застосування синтаксису sql у змінній query.
12
+ * @priority 5
13
+ * @type helper
14
+ * @alias contentList
15
+ * @example
16
+ * {{#contentList table="help.doc_function" query="type='api'" sql1=1 limit=1}}{{#each rows}}{{{JSON 2 this}}}{{/each}}{{/contentList}}
17
+ * @example
18
+ * {{#contentList table="help.doc_function" sql1=1 query="name like '%form%'" limit=1}}{{#each rows}}{{{JSON 2 this}}}{{/each}}{{/contentList}}
19
+ * @example
20
+ * {{#contentList table="help.article" sql=1 query="module='CORE'" limit=1}}{{#each rows}}{{{JSON 2 this}}}{{/each}}{{/contentList}}
21
+ * @param {String} table Таблиця в базі або конфіг таблиця
22
+ * @param {String} query Запит до бази
23
+ * @param {Number} limit Кількість рядків на сторінці
24
+ * @param {String} sql Вивід sql запиту
25
+ * @returns {String} Returns HTML
26
+ */
27
+ export default async function contentList(options) {
28
+ const { table, limit, query, order, sql, debug } = options.hash;
29
+ if (!table) { return 'Table undefined'; }
30
+
31
+ try {
32
+ const pg = getPG();
33
+
34
+ const hasBrackets = table.trim().startsWith('(') && table.trim().endsWith(')');
35
+
36
+ const where = `where ${query ? query : '1=1'}`;
37
+ const _limit = limit !== undefined && limit !== null ? Math.min(maxLimit, +limit) : 15;
38
+ const _order = order ? `order by ${order}` : '';
39
+
40
+ const SQL = `select *,${pg.pk[table] || '1'}::text from ${hasBrackets ? table + ' t' : table} ${where} ${_order} limit ${_limit}`;
41
+ const compiledSQL = Handlebars.compile(SQL)(options.data.root);
42
+
43
+ if (sql) {
44
+ return compiledSQL.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;').replaceAll('"', '&quot;')
45
+ .replaceAll("'", '&#039;');
46
+ }
47
+ const { rows } = await pg.query(compiledSQL);
48
+ const data = { rows, total: rows.length, ...options.data?.root };
49
+
50
+ if (debug) {
51
+ return JSON.stringify(data, null, 2);
52
+ }
53
+
54
+ return options.fn(data);
55
+ } catch (err) {
56
+ return `Сталася помилка, зверніться до відділу підтримки.<!-- err: ${err.toString()} -->`;
57
+ }
58
+ };
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Повертає пустий рядок замість змісту іншого хелпера при компіляції шаблону (сторінки). Пишеться на початку хелпера та дозволяє ігнорувати подальший зміст. Аналогічно коментуванню ділянок коду лапками, проте не погіршує зовнішній вигляд коду
3
+ *
4
+ * @summary Повертає пустий рядок замість змісту іншого хелпера при компіляції шаблону.
5
+ * @priority 4
6
+ * @alias empty
7
+ * @type helper
8
+ * @tag condition
9
+ * @example
10
+ * {{{empty widget.widget_adverts}}}
11
+ * @example
12
+ * {{{empty _hb template="change-password-email-template"}}}
13
+ * @example
14
+ * {{empty formatDate bt_on_work_date format="dd.MM.yy / hh:mi:sec"}}
15
+ * @example
16
+ * {{{empty mls 'alertinfo'}}}
17
+ * @returns {String} Returns HTML
18
+ */
19
+ export default function empty() {
20
+ return '';
21
+ };
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Перетинає два масиви
3
+ *
4
+ * @example
5
+ * // returns [1, 4, 5, 6]
6
+ * intersect([1,2,3,4,5,6],[1,4,5,6,7,8,9,11])
7
+ * @param {Array} a
8
+ * @param {Array} b
9
+ * @returns {Array} Returns new intersect array
10
+ */
11
+ function intersect(a, b) {
12
+ let aN = a; let bN = b;
13
+ if (b.length > a.length) {
14
+ [aN, bN] = [bN, aN];
15
+ }
16
+ return aN.filter((e) => bN.includes(e));
17
+ }
18
+
19
+ /**
20
+ * Створення шаблона або його частини внаслідок перевірки значення із веб-запиту та заздалегідь прописаного значення.
21
+ * Дозволяє змінювати наповнення сторінки через ряд перевірок. Є можливість внесення додаткової умови - що робити, коли умова не виконується.
22
+ *
23
+ * @summary Перевірка двох значень та виконання коду при виконанні умови, а також у всіх інших випадках.
24
+ * @priority 5
25
+ * @alias ifCond
26
+ * @type helper
27
+ * @tag condition
28
+ * @example
29
+ * {{#ifCond @root.req.domain 'in' 'help.softpro.ua,123'}} {{select user.uid data="get_full_uid"}} {{^}} Умова не виконана {{/ifCond}}
30
+ * @example
31
+ * {{#ifCond "1234567890" 'in' @root.user.group_list}} 1=1 {{^}} uid='{{uid}}' {{/ifCond}}
32
+ * @example
33
+ * {{#ifCond 'debug' 'in' @root.setting.core.setting}}Умова виконана{{^}}Не виконана умова{{/ifCond}}
34
+ * @param {Array} args Параметри для значень і умов
35
+ * @param {Array} args[0]] Перше значення
36
+ * @param {Array} args[1]] Оператор
37
+ * @param {Array} args[2]] Друге значення
38
+ * @returns {String} Returns HTML
39
+ */
40
+ export default function ifCond(v1, operator, v2, options) {
41
+ const __obj = this;
42
+
43
+ const isEmpty = (val) => val === null || val === undefined || (Array.isArray(val) && val.length === 0) || val === '';
44
+
45
+ switch (operator) {
46
+ case '==':
47
+ return (v1 == v2) ? options.fn(__obj) : options.inverse(__obj);
48
+ case '!=':
49
+ return (v1 != v2) ? options.fn(__obj) : options.inverse(__obj);
50
+ case '===':
51
+ return (v1 === v2) ? options.fn(__obj) : options.inverse(__obj);
52
+ case '!==':
53
+ return (v1 !== v2) ? options.fn(__obj) : options.inverse(__obj);
54
+ case '&&':
55
+ return (v1 && v2) ? options.fn(__obj) : options.inverse(__obj);
56
+ case '||':
57
+ return (v1 || v2) ? options.fn(__obj) : options.inverse(__obj);
58
+ case '<':
59
+ return (v1 < v2) ? options.fn(__obj) : options.inverse(__obj);
60
+ case '<=':
61
+ return (v1 <= v2) ? options.fn(__obj) : options.inverse(__obj);
62
+ case '>':
63
+ return (v1 > v2) ? options.fn(__obj) : options.inverse(__obj);
64
+ case '>=':
65
+ return (v1 >= v2) ? options.fn(__obj) : options.inverse(__obj);
66
+ case '&':
67
+ return (!isEmpty(v1) && !isEmpty(v2) && intersect(v1, v2).length !== 0)
68
+ ? options.fn(__obj)
69
+ : options.inverse(__obj);
70
+ case '!~':
71
+ return (v1 || '').indexOf(v2) === -1
72
+ ? options.fn(__obj)
73
+ : options.inverse(__obj);
74
+ case '~':
75
+ return (v1 || '').indexOf(v2) !== -1
76
+ ? options.fn(__obj)
77
+ : options.inverse(__obj);
78
+ case 'period':
79
+ return (!isEmpty(v1) && !isEmpty(v2) && new Date(v1) < new Date() && new Date(v2) > new Date())
80
+ ? options.fn(__obj)
81
+ : options.inverse(__obj);
82
+ case 'in': {
83
+ if (typeof v2 === 'string') v2 = v2.split(',').map(item => item.trim());
84
+ if (isEmpty(v1) || isEmpty(v2)) return options.inverse(__obj);
85
+
86
+ if (Array.isArray(v1)) {
87
+ return v1.some((value) => v2.includes(value.toString()))
88
+ ? options.fn(__obj)
89
+ : options.inverse(__obj);
90
+ }
91
+ return v2.includes(v1.toString())
92
+ ? options.fn(__obj)
93
+ : options.inverse(__obj);
94
+ }
95
+ case 'not in': {
96
+ if (typeof v2 === 'string') v2 = v2.split(',').map(item => item.trim());
97
+ if (isEmpty(v1) || isEmpty(v2)) return options.inverse(__obj);
98
+
99
+ return !v2.includes(v1.toString())
100
+ ? options.fn(__obj)
101
+ : options.inverse(__obj);
102
+ }
103
+ default:
104
+ return options.inverse(__obj);
105
+ }
106
+ }
@@ -0,0 +1,96 @@
1
+ function intersect(a, b) {
2
+ const aN = Array.isArray(a) ? a : typeof a === 'string' ? a.split(',') : [];
3
+ const bN = Array.isArray(b) ? b : typeof b === 'string' ? b.split(',') : [];
4
+
5
+ return aN.filter((e) => bN.includes(e));
6
+ }
7
+
8
+ /**
9
+ * Створення шаблона або його частини внаслідок виконання одразу декількох перевірок, кожна з яких повинна повернути true.
10
+ * Якщо хоча б одна умова не виконується, код у межах хелпера також не виконується.
11
+ *
12
+ * @summary Створення шаблона за перевіркою виконання декількох умов.
13
+ * @priority 4
14
+ * @type helper
15
+ * @tag condition
16
+ * @alias ifCondAnd
17
+ * @example
18
+ * {{#ifCondAnd 1 '==' 1 2 'in' '2,3' 2 'in' '1,2' }}1{{^}}2{{/ifCondAnd}}
19
+ * @descr Виконання перевірки порівняння параметрів та їх входження до певного масиву (множини)
20
+ * @example
21
+ */
22
+ export default function ifCondAnd() {
23
+ const args = Array.from(arguments);
24
+ const options = args.pop();
25
+
26
+ const conditions = [];
27
+ for (let i = 0; i < args.length; i += 3) {
28
+ const v1 = args[i];
29
+ const operator = args[i + 1];
30
+ let v2 = args[i + 2];
31
+
32
+ switch (operator) {
33
+ case '==':
34
+ conditions.push(v1 == v2);
35
+ break;
36
+ case '!=':
37
+ conditions.push(v1 != v2);
38
+ break;
39
+ case '===':
40
+ conditions.push(v1 === v2);
41
+ break;
42
+ case '!==':
43
+ conditions.push(v1 !== v2);
44
+ break;
45
+ case '&&':
46
+ conditions.push(v1 && v2);
47
+ break;
48
+ case '||':
49
+ conditions.push(v1 || v2);
50
+ break;
51
+ case '<':
52
+ conditions.push(v1 < v2);
53
+ break;
54
+ case '<=':
55
+ conditions.push(v1 <= v2);
56
+ break;
57
+ case '>':
58
+ conditions.push(v1 > v2);
59
+ break;
60
+ case '>=':
61
+ conditions.push(v1 >= v2);
62
+ break;
63
+ case '&':
64
+ conditions.push(intersect(v1, v2).length > 0);
65
+ break;
66
+ case '!~':
67
+ conditions.push((v1 || '').indexOf(v2) === -1);
68
+ break;
69
+ case '~':
70
+ conditions.push((v1 || '').indexOf(v2) !== -1);
71
+ break;
72
+ case 'period':
73
+ conditions.push(new Date(v1) < new Date() && new Date(v2) > new Date());
74
+ break;
75
+ case 'in': {
76
+ if (typeof v2 === 'string') v2 = v2.split(',').map(item => item.trim());
77
+ if (Array.isArray(v1)) {
78
+ conditions.push(v1.some((value) => v2.includes(value.toString())));
79
+ } else {
80
+ conditions.push(v2.includes(v1.toString()));
81
+ }
82
+ break;
83
+ }
84
+ case 'not in': {
85
+ if (typeof v2 === 'string') v2 = v2.split(',').map(item => item.trim());
86
+ conditions.push(!v2.includes(v1.toString()));
87
+ break;
88
+ }
89
+ default:
90
+ conditions.push(false);
91
+ break;
92
+ }
93
+ }
94
+
95
+ return conditions.every(Boolean) ? options.fn(this) : options.inverse(this);
96
+ }
@@ -0,0 +1,98 @@
1
+ function intersect(a, b) {
2
+ const aN = Array.isArray(a) ? a : typeof a === 'string' ? a.split(',') : [];
3
+ const bN = Array.isArray(b) ? b : typeof b === 'string' ? b.split(',') : [];
4
+
5
+ return aN.filter((e) => bN.includes(e));
6
+ }
7
+
8
+
9
+ /**
10
+ * Створення шаблона або його частини внаслідок виконання одразу декількох перевірок, хоча б одна з яких повинна повернути true
11
+ *
12
+ * @summary Створення шаблона за перевіркою "АБО". Повинна виконатись хоча б одна умова.
13
+ * @priority 4
14
+ * @type helper
15
+ * @tag condition
16
+ * @alias ifCondOr
17
+ * @example
18
+ * {{#ifCondOr 1 '==' 1 2 'in' '2,3' 2 'in' '1,2' }}Хоча б одна умова виконана{{^}}Жодна умова не була виконана{{/ifCondOr}}
19
+ * @descr Виконання перевірки порівняння параметрів та їх входження до певного масиву (множини)
20
+ * @example
21
+ * @param {Array} args Параметри для значень і умов
22
+ * @returns {String} Returns HTML
23
+ */
24
+ export default function ifCondOr() {
25
+ const args = Array.from(arguments);
26
+ const options = args.pop();
27
+
28
+ const conditions = [];
29
+ for (let i = 0; i < args.length; i += 3) {
30
+ const v1 = args[i];
31
+ const operator = args[i + 1];
32
+ let v2 = args[i + 2];
33
+
34
+ switch (operator) {
35
+ case '==':
36
+ conditions.push(v1 == v2);
37
+ break;
38
+ case '!=':
39
+ conditions.push(v1 != v2);
40
+ break;
41
+ case '===':
42
+ conditions.push(v1 === v2);
43
+ break;
44
+ case '!==':
45
+ conditions.push(v1 !== v2);
46
+ break;
47
+ case '&&':
48
+ conditions.push(v1 && v2);
49
+ break;
50
+ case '||':
51
+ conditions.push(v1 || v2);
52
+ break;
53
+ case '<':
54
+ conditions.push(v1 < v2);
55
+ break;
56
+ case '<=':
57
+ conditions.push(v1 <= v2);
58
+ break;
59
+ case '>':
60
+ conditions.push(v1 > v2);
61
+ break;
62
+ case '>=':
63
+ conditions.push(v1 >= v2);
64
+ break;
65
+ case '&':
66
+ conditions.push(intersect(v1, v2).length > 0);
67
+ break;
68
+ case '!~':
69
+ conditions.push((v1 || '').indexOf(v2) === -1);
70
+ break;
71
+ case '~':
72
+ conditions.push((v1 || '').indexOf(v2) !== -1);
73
+ break;
74
+ case 'period':
75
+ conditions.push(new Date(v1) < new Date() && new Date(v2) > new Date());
76
+ break;
77
+ case 'in': {
78
+ if (typeof v2 === 'string') v2 = v2.split(',').map(item => item.trim());
79
+ if (Array.isArray(v1)) {
80
+ conditions.push(v1.some((value) => v2.includes(value.toString())));
81
+ } else {
82
+ conditions.push(v2.includes(v1.toString()));
83
+ }
84
+ break;
85
+ }
86
+ case 'not in': {
87
+ if (typeof v2 === 'string') v2 = v2.split(',').map(item => item.trim());
88
+ conditions.push(!v2.includes(v1.toString()));
89
+ break;
90
+ }
91
+ default:
92
+ conditions.push(false);
93
+ break;
94
+ }
95
+ }
96
+
97
+ return conditions.some(Boolean) ? options.fn(this) : options.inverse(this);
98
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Складення двох значень з масива
3
+ *
4
+ * @summary Складення двох значень з масива. Є можливість використання у якості порядокового номеру
5
+ * @priority 3
6
+ * @type helper
7
+ * @alias inc
8
+ * @tag math
9
+ * @example
10
+ * {{{inc (coalesce @index 0)}}}
11
+ * @descr При використанні в межах циклу, дозволяє відображати віртуальний індекс
12
+ * @param {Array} args Масив чисел для складання
13
+ * @returns {String} Returns HTML
14
+ */
15
+ export default function inc(...args) {
16
+ const firstValue = args[0] || 0;
17
+ const secondValue = typeof args[1] === 'number' ? args[1] : 1;
18
+
19
+ return firstValue + secondValue;
20
+ }
21
+
@@ -0,0 +1,3 @@
1
+ export default function (data) {
2
+ return JSON.stringify(data, null, 2)
3
+ }
@@ -0,0 +1,66 @@
1
+ import qr from 'qrcode';
2
+
3
+ function isAllowedType(type) {
4
+ return Boolean({
5
+ png: 1, svg: 1, eps: 1, pdf: 1,
6
+ }[type]);
7
+ }
8
+
9
+ function isAllowedErrorCorrectionLevel(ecLevel) {
10
+ return Boolean({
11
+ L: 1, M: 1, Q: 1, H: 1,
12
+ }[ecLevel]);
13
+ }
14
+
15
+ /**
16
+ * Генерує зображення з QR кодом, що перенаправляє на посилання.
17
+ *
18
+ * @summary Генератор QR код. Є можливість задання відступу та встановлення розширення вихідного зображення
19
+ * @priority 3
20
+ * @type helper
21
+ * @tag file
22
+ * @alias qrcode-generator-base64
23
+ * @example
24
+ * {{{qrcode-generator-base64 text=@root.req.domain type='png' margin=1}}}
25
+ * @param {String} text Змінна, що містить посилання на файл або сторінку
26
+ * @param {String} type Розширення зображення
27
+ * @param {String} ec_level (?)
28
+ * @param {Number} margin Відступ
29
+ * @param {Number} size Розмір
30
+ * @returns {String} Returns HTML
31
+ */
32
+ export default async function qrcodeGenerator({ hash }) {
33
+ const text = String(hash?.text || '');
34
+ const type = String(hash?.type || 'png').toLowerCase();
35
+ const ecLevel = String(hash?.ec_level || 'M').toUpperCase();
36
+ const margin = Math.abs(hash?.margin || (type === 'png' ? 4 : 1));
37
+ let size;
38
+
39
+ if (type === 'png') {
40
+ size = Math.abs(hash?.size) || 5;
41
+ } else if (type === 'svg') {
42
+ size = Math.abs(hash?.size) || undefined;
43
+ }
44
+
45
+ if (!text) {
46
+ return 'Qr text is required.';
47
+ }
48
+ if (!isAllowedType(type)) {
49
+ return `Type "${type}" is not allowed.`;
50
+ }
51
+ if (!isAllowedErrorCorrectionLevel(ecLevel)) {
52
+ return `Error correction level "${ecLevel}" is not allowed.`;
53
+ }
54
+
55
+ const options = (type === 'png' || type === 'svg') ? {
56
+ ec_level: ecLevel, type, margin, size,
57
+ } : { ec_level: ecLevel, type, margin };
58
+
59
+ try {
60
+ const qrString = await qr.toDataURL(text, options);
61
+
62
+ return qrString;
63
+ } catch (err) {
64
+ return err.toString();
65
+ }
66
+ };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Округлення числа до певної точності
3
+ *
4
+ * @summary Округлення числа до певної точності. Є можливість округлення до низу.
5
+ * @priority 3
6
+ * @type helper
7
+ * @alias round
8
+ * @tag format
9
+ * @example
10
+ * {{round 3.14159265359 dec="6"}}
11
+ * @descr Округлення числа із змінної x до 6 знаків після роздільника
12
+ * @param {Number} dec Підрахування числа після роздільника
13
+ * @param {Any} floor False - округлення до низу
14
+ * @param {Number} data Передане число
15
+ * @returns {String} Returns HTML
16
+ */
17
+ export default function round(data, options) {
18
+ const _data = parseFloat(data);
19
+ if (isNaN(_data)) return '';
20
+
21
+ const dec = options.hash.dec ? parseInt(options.hash.dec, 10) : 0;
22
+
23
+ if (options.hash.floor) {
24
+ return Math.floor(_data).toFixed(dec);
25
+ }
26
+
27
+ return _data.toFixed(dec);
28
+ };
@@ -0,0 +1,50 @@
1
+ import getPG from '../../plugins/pg/funcs/getPG.js';
2
+ import getSelect from '../../plugins/table/funcs/getSelect.js';
3
+
4
+ const pg = getPG();
5
+
6
+ export default async function select(ids, options) {
7
+ if (!ids && ids !== false && ids !== 0) {
8
+ return ids;
9
+ }
10
+ if (Array.isArray(ids) && ids.length === 0) {
11
+ return '';
12
+ }
13
+
14
+ const data = options.hash?.data;
15
+ if (!data) return '';
16
+
17
+ try {
18
+ const idsArray = Array.isArray(ids) ? ids : [ids];
19
+
20
+ const classifier = await getSelect(data);
21
+
22
+ if (!classifier) return `Не знайдено класифікатор ${data}`;
23
+
24
+ if (classifier.sql && typeof classifier.sql === 'string') {
25
+ const metaQuery = `SELECT * FROM (${classifier.sql})q LIMIT 0`;
26
+ const meta = await pg.query(metaQuery);
27
+ const idColumn = meta.fields[0].name;
28
+ const textColumn = meta.fields[1].name;
29
+
30
+ const sql = `SELECT "${idColumn}" AS id, "${textColumn}" AS text FROM (${classifier.sql}) q WHERE "${idColumn}" = ANY($1::text[])`;
31
+ const values = [idsArray.map(id => String(id))];
32
+
33
+ const { rows } = await pg.query(sql, values);
34
+ classifier.arr = rows;
35
+ }
36
+
37
+ if (!Array.isArray(classifier?.arr) || !classifier.arr.length || !classifier.arr.every(item => typeof item === 'object' && item !== null)) {
38
+ return ids;
39
+ }
40
+
41
+ const results = idsArray.map(id => {
42
+ const result = classifier.arr.find(el => String(el.id) == String(id));
43
+ return result ? result.text : '';
44
+ });
45
+
46
+ return results.filter(Boolean).join(', ');
47
+ } catch (error) {
48
+ return `Сталася помилка.<!-- err: ${error.toString()} -->`;
49
+ }
50
+ }
@@ -1,5 +1,36 @@
1
- import { handlebars } from '@opengis/fastify-hb/utils.js';
2
- import { logger } from '../../utils.js';
1
+ import { logger, handlebars } from '../../utils.js';
2
+
3
+ // funcs
4
+ import _math from './funcs/_math.js'
5
+ import ifCond from './funcs/ifCond.js'
6
+ import ifCondAnd from './funcs/ifCondAnd.js'
7
+ import ifCondOr from './funcs/ifCondOr.js'
8
+ import qrcode from './funcs/qrcode.js'
9
+ import select from './funcs/select.js'
10
+ import json from './funcs/json.js'
11
+ import empty from './funcs/empty.js'
12
+ import round from './funcs/round.js'
13
+ import contentList from './funcs/contentList.js'
14
+ import inc from './funcs/inc.js'
15
+
16
+ // format
17
+ import formatAuto from './format/formatAuto.js'
18
+ import formatDate from './format/formatDate.js'
19
+ import formatDigit from './format/formatDigit.js'
20
+ import formatNum from './format/formatNum.js'
21
+ import formatNumber from './format/formatNumber.js'
22
+ import formatRelative from './format/formatRelative.js'
23
+ import formatUnit from './format/formatUnit.js'
24
+ import num_format from './format/num_format.js'
25
+ import set from './format/set.js'
26
+
27
+ // string
28
+ import str_replace from './string/str_replace.js'
29
+ import coalesce from './string/coalesce.js'
30
+ import concat from './string/concat.js'
31
+ import split from './string/split.js'
32
+ import translit from './string/translit.js'
33
+ import substr from './string/substr.js'
3
34
 
4
35
  function getKeysRecursive(obj, prefix = '') {
5
36
  if (!obj || typeof obj !== 'object' || obj?.constructor?.name !== 'Object') return [];
@@ -26,4 +57,40 @@ export default async function helpers() {
26
57
  }
27
58
  return args;
28
59
  });
29
- }
60
+
61
+ // format
62
+ handlebars.registerHelper('formatAuto', formatAuto);
63
+ handlebars.registerHelper('formatDate', formatDate);
64
+ handlebars.registerHelper('formatDigit', formatDigit);
65
+ handlebars.registerHelper('formatNum', formatNum);
66
+ handlebars.registerHelper('formatNumber', formatNumber);
67
+ handlebars.registerHelper('formatRelative', formatRelative);
68
+ handlebars.registerHelper('formatUnit', formatUnit);
69
+ handlebars.registerHelper('num_format', num_format);
70
+ handlebars.registerHelper('set', set);
71
+
72
+ // string
73
+ handlebars.registerHelper('str_replace', str_replace);
74
+ handlebars.registerHelper('coalesce', coalesce);
75
+ handlebars.registerHelper('concat', concat);
76
+ handlebars.registerHelper('split', split);
77
+ handlebars.registerHelper('translit', translit);
78
+ handlebars.registerHelper('substr', substr);
79
+ handlebars.registerHelper('mls', (value) => value);
80
+
81
+ // funcs
82
+ handlebars.registerHelper('json', json);
83
+ handlebars.registerHelper('_math', _math);
84
+ handlebars.registerHelper('ifCond', ifCond);
85
+ handlebars.registerHelper('ifCondAnd', ifCondAnd);
86
+ handlebars.registerHelper('ifCondOr', ifCondOr);
87
+ handlebars.registerHelper('select', select);
88
+ handlebars.registerHelper('empty', empty);
89
+ handlebars.registerHelper('round', round);
90
+ handlebars.registerHelper('contentList', contentList);
91
+ handlebars.registerHelper('inc', inc);
92
+ handlebars.registerHelper('qrcode', qrcode);
93
+
94
+ // Підтримка старого коду
95
+ handlebars.registerHelper('qrcode-generator-base64', qrcode);
96
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Повертає перший елемент, який не є пустим або відсутнім
3
+ *
4
+ * @summary Повертає перший не null елемент. Є можливість застосування разом із іншими хелперами.
5
+ * @priority 4
6
+ * @type helper
7
+ * @alias coalesce
8
+ * @tag string
9
+ * @example
10
+ * {{coalesce color '#337ab7'}}
11
+ * @descr Повертає значення змінної color або '#337ab7', якщо така змінна відсутня
12
+ * @example
13
+ * {{coalesce @root.lang 'ua'}}
14
+ * @example
15
+ * {{coalesce (select uid data="user_id") (select uid data="uid")}}
16
+ * @descr За відсутості співпадіння в межах пошуку по першому класифікатору, виводить результат другого селекту
17
+ * @param {Array} args Масив аргументів для фільтрування
18
+ * @returns {String} Return Html
19
+ */
20
+
21
+ export default function coalesce(...args) {
22
+ const options = args.pop();
23
+
24
+ for (const arg of args) {
25
+ if (arg != null && arg !== '') {
26
+ return arg;
27
+ }
28
+ }
29
+
30
+ return options.fn ? options.fn(this) : (options.inverse ? options.inverse(this) : '');
31
+ }