@escapenavigator/utils 2.0.24 → 2.0.26
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.
- package/dist/date.js +5 -0
- package/dist/format-amount/index.js +32 -18
- package/dist/get-prices.d.ts +6 -0
- package/dist/get-prices.js +21 -1
- package/dist/get-prices.spec.d.ts +1 -0
- package/dist/get-prices.spec.js +31 -0
- package/dist/mask-contacts.d.ts +5 -5
- package/dist/mask-contacts.js +28 -11
- package/dist/mask-contacts.spec.d.ts +1 -0
- package/dist/mask-contacts.spec.js +21 -0
- package/dist/role-contact-permissions.d.ts +3 -2
- package/dist/role-contact-permissions.js +3 -2
- package/package.json +3 -3
package/dist/date.js
CHANGED
|
@@ -132,6 +132,11 @@ exports.DISPLAY_DATE_LOCALE_BY_LANG = {
|
|
|
132
132
|
ro: 'ro-RO',
|
|
133
133
|
lt: 'lt-LT',
|
|
134
134
|
he: 'he-IL',
|
|
135
|
+
hr: 'hr-HR',
|
|
136
|
+
ja: 'ja-JP',
|
|
137
|
+
zh: 'zh-TW',
|
|
138
|
+
sr: 'sr-RS',
|
|
139
|
+
no: 'nb-NO',
|
|
135
140
|
};
|
|
136
141
|
function resolveDisplayLocaleTag(lang) {
|
|
137
142
|
const key = lang?.split('-')[0]?.toLowerCase() || 'en';
|
|
@@ -7,30 +7,37 @@ const getCurrency = (currency) => {
|
|
|
7
7
|
[profile_currency_1.ProfileCurrencyEnum.USD]: '$',
|
|
8
8
|
[profile_currency_1.ProfileCurrencyEnum.EUR]: '€',
|
|
9
9
|
[profile_currency_1.ProfileCurrencyEnum.GBP]: '£',
|
|
10
|
-
[profile_currency_1.ProfileCurrencyEnum.CHF]: 'CHF',
|
|
11
|
-
[profile_currency_1.ProfileCurrencyEnum.SEK]: 'kr',
|
|
12
|
-
[profile_currency_1.ProfileCurrencyEnum.DKK]: 'kr',
|
|
13
|
-
[profile_currency_1.ProfileCurrencyEnum.CZK]: 'Kč',
|
|
14
|
-
[profile_currency_1.ProfileCurrencyEnum.PLN]: 'zł',
|
|
15
|
-
[profile_currency_1.ProfileCurrencyEnum.HUF]: 'Ft',
|
|
10
|
+
[profile_currency_1.ProfileCurrencyEnum.CHF]: 'CHF',
|
|
11
|
+
[profile_currency_1.ProfileCurrencyEnum.SEK]: 'kr',
|
|
12
|
+
[profile_currency_1.ProfileCurrencyEnum.DKK]: 'kr',
|
|
13
|
+
[profile_currency_1.ProfileCurrencyEnum.CZK]: 'Kč',
|
|
14
|
+
[profile_currency_1.ProfileCurrencyEnum.PLN]: 'zł',
|
|
15
|
+
[profile_currency_1.ProfileCurrencyEnum.HUF]: 'Ft',
|
|
16
16
|
[profile_currency_1.ProfileCurrencyEnum.RUB]: 'руб.',
|
|
17
17
|
[profile_currency_1.ProfileCurrencyEnum.BGN]: 'лв',
|
|
18
18
|
[profile_currency_1.ProfileCurrencyEnum.CAD]: 'C$',
|
|
19
19
|
[profile_currency_1.ProfileCurrencyEnum.AUD]: '$',
|
|
20
|
-
[profile_currency_1.ProfileCurrencyEnum.NOK]: 'kr',
|
|
21
|
-
[profile_currency_1.ProfileCurrencyEnum.HRK]: 'kn',
|
|
22
|
-
[profile_currency_1.ProfileCurrencyEnum.RON]: 'lei',
|
|
23
|
-
[profile_currency_1.ProfileCurrencyEnum.RSD]: 'дин',
|
|
24
|
-
[profile_currency_1.ProfileCurrencyEnum.ALL]: 'L',
|
|
25
|
-
[profile_currency_1.ProfileCurrencyEnum.MKD]: 'ден',
|
|
20
|
+
[profile_currency_1.ProfileCurrencyEnum.NOK]: 'kr',
|
|
21
|
+
[profile_currency_1.ProfileCurrencyEnum.HRK]: 'kn',
|
|
22
|
+
[profile_currency_1.ProfileCurrencyEnum.RON]: 'lei',
|
|
23
|
+
[profile_currency_1.ProfileCurrencyEnum.RSD]: 'дин',
|
|
24
|
+
[profile_currency_1.ProfileCurrencyEnum.ALL]: 'L',
|
|
25
|
+
[profile_currency_1.ProfileCurrencyEnum.MKD]: 'ден',
|
|
26
26
|
[profile_currency_1.ProfileCurrencyEnum.BAM]: 'KM',
|
|
27
|
-
[profile_currency_1.ProfileCurrencyEnum.TRY]: '',
|
|
28
|
-
[profile_currency_1.ProfileCurrencyEnum.GEL]: '',
|
|
29
|
-
[profile_currency_1.ProfileCurrencyEnum.MDL]: '',
|
|
30
|
-
[profile_currency_1.ProfileCurrencyEnum.BYN]: '',
|
|
31
|
-
[profile_currency_1.ProfileCurrencyEnum.UAH]: '',
|
|
32
|
-
[profile_currency_1.ProfileCurrencyEnum.ISK]: '',
|
|
27
|
+
[profile_currency_1.ProfileCurrencyEnum.TRY]: '₺',
|
|
28
|
+
[profile_currency_1.ProfileCurrencyEnum.GEL]: '₾',
|
|
29
|
+
[profile_currency_1.ProfileCurrencyEnum.MDL]: 'L',
|
|
30
|
+
[profile_currency_1.ProfileCurrencyEnum.BYN]: 'Br',
|
|
31
|
+
[profile_currency_1.ProfileCurrencyEnum.UAH]: '₴',
|
|
32
|
+
[profile_currency_1.ProfileCurrencyEnum.ISK]: 'kr',
|
|
33
33
|
[profile_currency_1.ProfileCurrencyEnum.ILS]: '₪',
|
|
34
|
+
[profile_currency_1.ProfileCurrencyEnum.NZD]: '$',
|
|
35
|
+
[profile_currency_1.ProfileCurrencyEnum.ARS]: '$',
|
|
36
|
+
[profile_currency_1.ProfileCurrencyEnum.BRL]: 'R$',
|
|
37
|
+
[profile_currency_1.ProfileCurrencyEnum.TWD]: 'NT$',
|
|
38
|
+
[profile_currency_1.ProfileCurrencyEnum.MXN]: '$',
|
|
39
|
+
[profile_currency_1.ProfileCurrencyEnum.JPY]: '¥',
|
|
40
|
+
[profile_currency_1.ProfileCurrencyEnum.SGD]: '$',
|
|
34
41
|
};
|
|
35
42
|
return res[currency] || currency;
|
|
36
43
|
};
|
|
@@ -39,6 +46,13 @@ const currencyFirst = (currency) => currency === 'USD' ||
|
|
|
39
46
|
currency === 'GBP' ||
|
|
40
47
|
currency === 'CAD' ||
|
|
41
48
|
currency === 'AUD' ||
|
|
49
|
+
currency === 'NZD' ||
|
|
50
|
+
currency === 'ARS' ||
|
|
51
|
+
currency === 'BRL' ||
|
|
52
|
+
currency === 'TWD' ||
|
|
53
|
+
currency === 'MXN' ||
|
|
54
|
+
currency === 'JPY' ||
|
|
55
|
+
currency === 'SGD' ||
|
|
42
56
|
currency === 'TRY' ||
|
|
43
57
|
currency === 'GEL' ||
|
|
44
58
|
currency === 'UAH';
|
package/dist/get-prices.d.ts
CHANGED
|
@@ -5,4 +5,10 @@ export declare const getPricesPlayersArray: (price?: Record<string, number>) =>
|
|
|
5
5
|
* не должны рендериться в пикерах и не должны проходить валидацию на бэке.
|
|
6
6
|
*/
|
|
7
7
|
export declare const getAvailablePlayersArray: (price?: Record<string, number>) => string[];
|
|
8
|
+
/**
|
|
9
|
+
* Если групповая цена линейна по числу участников (например 2→4000, 3→6000
|
|
10
|
+
* → 2000 за человека), возвращает эту ставку в минорных единицах.
|
|
11
|
+
* Иначе `null` — UI должен показать полную детализацию по составам.
|
|
12
|
+
*/
|
|
13
|
+
export declare const getFlatPerPersonAmount: (price?: Record<string, number>) => number | null;
|
|
8
14
|
export declare const getPricesArray: (price?: Record<string, number>) => number[];
|
package/dist/get-prices.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getPricesArray = exports.getAvailablePlayersArray = exports.getPricesPlayersArray = void 0;
|
|
3
|
+
exports.getPricesArray = exports.getFlatPerPersonAmount = exports.getAvailablePlayersArray = exports.getPricesPlayersArray = void 0;
|
|
4
4
|
const getPricesPlayersArray = (price) => {
|
|
5
5
|
if (!price)
|
|
6
6
|
return [];
|
|
@@ -22,6 +22,26 @@ const getAvailablePlayersArray = (price) => {
|
|
|
22
22
|
.sort((a, b) => +a - +b);
|
|
23
23
|
};
|
|
24
24
|
exports.getAvailablePlayersArray = getAvailablePlayersArray;
|
|
25
|
+
/**
|
|
26
|
+
* Если групповая цена линейна по числу участников (например 2→4000, 3→6000
|
|
27
|
+
* → 2000 за человека), возвращает эту ставку в минорных единицах.
|
|
28
|
+
* Иначе `null` — UI должен показать полную детализацию по составам.
|
|
29
|
+
*/
|
|
30
|
+
const getFlatPerPersonAmount = (price) => {
|
|
31
|
+
if (!price)
|
|
32
|
+
return null;
|
|
33
|
+
const entries = Object.entries(price)
|
|
34
|
+
.filter(([ppl, amount]) => !Number.isNaN(+ppl) && Number(amount) > 0)
|
|
35
|
+
.map(([ppl, amount]) => [+ppl, Number(amount)]);
|
|
36
|
+
if (entries.length < 2)
|
|
37
|
+
return null;
|
|
38
|
+
const perPerson = entries[0][1] / entries[0][0];
|
|
39
|
+
if (!Number.isFinite(perPerson) || perPerson <= 0)
|
|
40
|
+
return null;
|
|
41
|
+
const isFlat = entries.every(([count, amount]) => Math.abs(amount / count - perPerson) < 0.5);
|
|
42
|
+
return isFlat ? Math.round(perPerson) : null;
|
|
43
|
+
};
|
|
44
|
+
exports.getFlatPerPersonAmount = getFlatPerPersonAmount;
|
|
25
45
|
const getPricesArray = (price) => {
|
|
26
46
|
if (!price)
|
|
27
47
|
return [];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const get_prices_1 = require("./get-prices");
|
|
4
|
+
describe('getFlatPerPersonAmount', () => {
|
|
5
|
+
it('returns per-person amount when group prices scale linearly', () => {
|
|
6
|
+
expect((0, get_prices_1.getFlatPerPersonAmount)({ 2: 4000, 3: 6000, 4: 8000 })).toBe(2000);
|
|
7
|
+
});
|
|
8
|
+
it('returns null when prices are not a flat per-person rate', () => {
|
|
9
|
+
expect((0, get_prices_1.getFlatPerPersonAmount)({ 2: 5000, 3: 6000, 4: 7000 })).toBeNull();
|
|
10
|
+
});
|
|
11
|
+
it('returns null for a single player count', () => {
|
|
12
|
+
expect((0, get_prices_1.getFlatPerPersonAmount)({ 2: 4000 })).toBeNull();
|
|
13
|
+
});
|
|
14
|
+
it('ignores non-numeric and zero-price keys', () => {
|
|
15
|
+
expect((0, get_prices_1.getFlatPerPersonAmount)({
|
|
16
|
+
2: 4000,
|
|
17
|
+
3: 6000,
|
|
18
|
+
child: 1000,
|
|
19
|
+
5: 0,
|
|
20
|
+
})).toBe(2000);
|
|
21
|
+
});
|
|
22
|
+
it('returns null for empty / missing price map', () => {
|
|
23
|
+
expect((0, get_prices_1.getFlatPerPersonAmount)()).toBeNull();
|
|
24
|
+
expect((0, get_prices_1.getFlatPerPersonAmount)({})).toBeNull();
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
describe('getAvailablePlayersArray', () => {
|
|
28
|
+
it('skips zero-price keys', () => {
|
|
29
|
+
expect((0, get_prices_1.getAvailablePlayersArray)({ 1: 0, 2: 5000, 3: 6000 })).toEqual(['2', '3']);
|
|
30
|
+
});
|
|
31
|
+
});
|
package/dist/mask-contacts.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/** Символ-заполнитель для маскированных контактов клиента. */
|
|
2
2
|
export declare const MASK_CHAR = "\u2022";
|
|
3
3
|
/**
|
|
4
|
-
* Маскирует email, оставляя
|
|
5
|
-
* `john.doe@mail.com` → `
|
|
6
|
-
* возвращаются как
|
|
4
|
+
* Маскирует email, оставляя начало и конец локальной части + домен:
|
|
5
|
+
* `john.doe@mail.com` → `jo•••••e@mail.com`.
|
|
6
|
+
* Пустые/невалидные значения возвращаются как есть.
|
|
7
7
|
*/
|
|
8
8
|
export declare const maskEmail: (email?: string | null) => string;
|
|
9
9
|
/**
|
|
10
|
-
* Маскирует телефон, оставляя
|
|
11
|
-
* `+4915112345678` → `+49•••••••
|
|
10
|
+
* Маскирует телефон, оставляя первые 2 и последние 4 цифры:
|
|
11
|
+
* `+4915112345678` → `+49•••••••5678`. Сохраняет ведущий `+`.
|
|
12
12
|
*/
|
|
13
13
|
export declare const maskPhone: (phone?: string | null) => string;
|
|
14
14
|
/** Похоже ли значение на уже замаскированное (содержит символ-заполнитель). */
|
package/dist/mask-contacts.js
CHANGED
|
@@ -4,9 +4,9 @@ exports.isMaskedContact = exports.maskPhone = exports.maskEmail = exports.MASK_C
|
|
|
4
4
|
/** Символ-заполнитель для маскированных контактов клиента. */
|
|
5
5
|
exports.MASK_CHAR = '•';
|
|
6
6
|
/**
|
|
7
|
-
* Маскирует email, оставляя
|
|
8
|
-
* `john.doe@mail.com` → `
|
|
9
|
-
* возвращаются как
|
|
7
|
+
* Маскирует email, оставляя начало и конец локальной части + домен:
|
|
8
|
+
* `john.doe@mail.com` → `jo•••••e@mail.com`.
|
|
9
|
+
* Пустые/невалидные значения возвращаются как есть.
|
|
10
10
|
*/
|
|
11
11
|
const maskEmail = (email) => {
|
|
12
12
|
if (!email)
|
|
@@ -19,13 +19,24 @@ const maskEmail = (email) => {
|
|
|
19
19
|
}
|
|
20
20
|
const local = trimmed.slice(0, at);
|
|
21
21
|
const domain = trimmed.slice(at);
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
if (local.length === 1) {
|
|
23
|
+
return `${local[0]}${exports.MASK_CHAR.repeat(3)}${domain}`;
|
|
24
|
+
}
|
|
25
|
+
if (local.length === 2) {
|
|
26
|
+
return `${local}${exports.MASK_CHAR.repeat(2)}${domain}`;
|
|
27
|
+
}
|
|
28
|
+
if (local.length === 3) {
|
|
29
|
+
return `${local.slice(0, 2)}${exports.MASK_CHAR}${local[2]}${domain}`;
|
|
30
|
+
}
|
|
31
|
+
const prefix = local.slice(0, 2);
|
|
32
|
+
const suffix = local.slice(-1);
|
|
33
|
+
const maskedCount = Math.max(local.length - 3, 2);
|
|
34
|
+
return `${prefix}${exports.MASK_CHAR.repeat(maskedCount)}${suffix}${domain}`;
|
|
24
35
|
};
|
|
25
36
|
exports.maskEmail = maskEmail;
|
|
26
37
|
/**
|
|
27
|
-
* Маскирует телефон, оставляя
|
|
28
|
-
* `+4915112345678` → `+49•••••••
|
|
38
|
+
* Маскирует телефон, оставляя первые 2 и последние 4 цифры:
|
|
39
|
+
* `+4915112345678` → `+49•••••••5678`. Сохраняет ведущий `+`.
|
|
29
40
|
*/
|
|
30
41
|
const maskPhone = (phone) => {
|
|
31
42
|
if (!phone)
|
|
@@ -33,12 +44,18 @@ const maskPhone = (phone) => {
|
|
|
33
44
|
const trimmed = String(phone).trim();
|
|
34
45
|
const hasPlus = trimmed.startsWith('+');
|
|
35
46
|
const digits = trimmed.replace(/\D/g, '');
|
|
36
|
-
if (digits.length <=
|
|
47
|
+
if (digits.length <= 4) {
|
|
37
48
|
return `${hasPlus ? '+' : ''}${exports.MASK_CHAR.repeat(Math.max(digits.length, 3))}`;
|
|
38
49
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
50
|
+
if (digits.length <= 6) {
|
|
51
|
+
const visible = digits.slice(-2);
|
|
52
|
+
const maskedCount = digits.length - 2;
|
|
53
|
+
return `${hasPlus ? '+' : ''}${exports.MASK_CHAR.repeat(maskedCount)}${visible}`;
|
|
54
|
+
}
|
|
55
|
+
const prefix = digits.slice(0, 2);
|
|
56
|
+
const suffix = digits.slice(-4);
|
|
57
|
+
const maskedCount = digits.length - 6;
|
|
58
|
+
return `${hasPlus ? '+' : ''}${prefix}${exports.MASK_CHAR.repeat(maskedCount)}${suffix}`;
|
|
42
59
|
};
|
|
43
60
|
exports.maskPhone = maskPhone;
|
|
44
61
|
/** Похоже ли значение на уже замаскированное (содержит символ-заполнитель). */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const mask_contacts_1 = require("./mask-contacts");
|
|
4
|
+
describe('maskEmail', () => {
|
|
5
|
+
it('keeps prefix, suffix and domain for identification', () => {
|
|
6
|
+
expect((0, mask_contacts_1.maskEmail)('john.doe@mail.com')).toBe('jo•••••e@mail.com');
|
|
7
|
+
});
|
|
8
|
+
it('handles short local parts', () => {
|
|
9
|
+
expect((0, mask_contacts_1.maskEmail)('ab@mail.com')).toBe('ab••@mail.com');
|
|
10
|
+
expect((0, mask_contacts_1.maskEmail)('abc@mail.com')).toBe('ab•c@mail.com');
|
|
11
|
+
expect((0, mask_contacts_1.maskEmail)('a@mail.com')).toBe('a•••@mail.com');
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
describe('maskPhone', () => {
|
|
15
|
+
it('keeps country prefix and last 4 digits', () => {
|
|
16
|
+
expect((0, mask_contacts_1.maskPhone)('+4915112345678')).toBe('+49•••••••5678');
|
|
17
|
+
});
|
|
18
|
+
it('falls back to last 2 digits for short numbers', () => {
|
|
19
|
+
expect((0, mask_contacts_1.maskPhone)('+49123')).toBe('+•••23');
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { RoleRO } from '@escapenavigator/types/dist/role/role.ro';
|
|
2
2
|
export type RoleContactPermissions = Pick<RoleRO, 'totalAccess' | 'canRevealClientContacts'>;
|
|
3
3
|
/**
|
|
4
|
-
* Право видеть полные контакты клиента (телефон/email) без
|
|
5
|
-
* Владелец (`totalAccess`) видит всегда; остальным
|
|
4
|
+
* Право видеть полные контакты клиента (телефон/email) без маскировки
|
|
5
|
+
* в ответах CRM API. Владелец (`totalAccess`) видит всегда; остальным
|
|
6
|
+
* нужен явный флаг `canRevealClientContacts` («Видит контакты клиентов»).
|
|
6
7
|
*/
|
|
7
8
|
export declare const canRevealClientContacts: (role?: RoleContactPermissions | null) => boolean;
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.canRevealClientContacts = void 0;
|
|
4
4
|
/**
|
|
5
|
-
* Право видеть полные контакты клиента (телефон/email) без
|
|
6
|
-
* Владелец (`totalAccess`) видит всегда; остальным
|
|
5
|
+
* Право видеть полные контакты клиента (телефон/email) без маскировки
|
|
6
|
+
* в ответах CRM API. Владелец (`totalAccess`) видит всегда; остальным
|
|
7
|
+
* нужен явный флаг `canRevealClientContacts` («Видит контакты клиентов»).
|
|
7
8
|
*/
|
|
8
9
|
const canRevealClientContacts = (role) => !!role?.totalAccess || !!role?.canRevealClientContacts;
|
|
9
10
|
exports.canRevealClientContacts = canRevealClientContacts;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@escapenavigator/utils",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.26",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"test": "jest"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@escapenavigator/types": "^2.0.
|
|
17
|
+
"@escapenavigator/types": "^2.0.25",
|
|
18
18
|
"axios": "^0.21.4",
|
|
19
19
|
"class-transformer": "^0.5.1",
|
|
20
20
|
"class-validator": "^0.13.2",
|
|
@@ -29,5 +29,5 @@
|
|
|
29
29
|
"ts-jest": "^29.1.1",
|
|
30
30
|
"typescript": "^5.6"
|
|
31
31
|
},
|
|
32
|
-
"gitHead": "
|
|
32
|
+
"gitHead": "5d2c9331c4cbd050b84f22bdc1e01e25dc3549fa"
|
|
33
33
|
}
|