@pushwoosh/dumb-components 1.1.133 → 1.1.134
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/Calendar/CalendarDropdown/CalendarDropdown.js +15 -3
- package/Calendar/CalendarSuperForm/components/CalendarTimeAndTimezoneSection/CalendarTimeAndTimezoneSection.js +14 -4
- package/Calendar/polyglot/index.d.ts +2 -0
- package/Calendar/polyglot/index.js +10 -0
- package/Calendar/polyglot/messages_en.d.ts +3 -0
- package/Calendar/polyglot/messages_en.js +11 -0
- package/Calendar/polyglot/messages_ru.d.ts +3 -0
- package/Calendar/polyglot/messages_ru.js +11 -0
- package/Calendar/polyglot/types.d.ts +14 -0
- package/Calendar/polyglot/types.js +1 -0
- package/FrequencyCapping/FrequencyCappingEdit.js +7 -2
- package/FrequencyCapping/FrequencyCappingInApps.js +7 -2
- package/FrequencyCapping/FrequencyCappingReadonly.js +5 -2
- package/FrequencyCapping/components/Description/index.js +3 -1
- package/FrequencyCapping/components/Settings/EditCappingSettings.js +10 -3
- package/FrequencyCapping/components/Settings/EditInAppCappingSettings.js +25 -8
- package/FrequencyCapping/helpers/getCappingChipText.d.ts +2 -1
- package/FrequencyCapping/helpers/getCappingChipText.js +13 -15
- package/FrequencyCapping/helpers/getDescriptionMessages.d.ts +2 -1
- package/FrequencyCapping/helpers/getDescriptionMessages.js +42 -22
- package/FrequencyCapping/helpers/getGlobalCappingLinkText.d.ts +2 -0
- package/FrequencyCapping/helpers/getGlobalCappingLinkText.js +7 -0
- package/FrequencyCapping/helpers/getReadonlyCappingHelpers.d.ts +2 -1
- package/FrequencyCapping/helpers/getReadonlyCappingHelpers.js +27 -15
- package/FrequencyCapping/polyglot/index.d.ts +4 -0
- package/FrequencyCapping/polyglot/index.js +10 -0
- package/FrequencyCapping/polyglot/messages_en.d.ts +3 -0
- package/FrequencyCapping/polyglot/messages_en.js +33 -0
- package/FrequencyCapping/polyglot/messages_ru.d.ts +3 -0
- package/FrequencyCapping/polyglot/messages_ru.js +33 -0
- package/FrequencyCapping/polyglot/types.d.ts +46 -0
- package/FrequencyCapping/polyglot/types.js +1 -0
- package/SendRate/SendRateEdit.js +7 -2
- package/SendRate/SendRateReadonly.js +5 -2
- package/SendRate/components/Condition/index.js +8 -1
- package/SendRate/components/Description/index.js +20 -6
- package/SendRate/components/Settings/index.js +7 -2
- package/SendRate/helpers/getGlobalSendRateLinkText.d.ts +2 -0
- package/SendRate/helpers/getGlobalSendRateLinkText.js +7 -0
- package/SendRate/helpers/getReadonlySendRateHelpers.d.ts +2 -1
- package/SendRate/helpers/getReadonlySendRateHelpers.js +20 -11
- package/SendRate/polyglot/index.d.ts +4 -0
- package/SendRate/polyglot/index.js +10 -0
- package/SendRate/polyglot/messages_en.d.ts +3 -0
- package/SendRate/polyglot/messages_en.js +20 -0
- package/SendRate/polyglot/messages_ru.d.ts +3 -0
- package/SendRate/polyglot/messages_ru.js +20 -0
- package/SendRate/polyglot/types.d.ts +23 -0
- package/SendRate/polyglot/types.js +1 -0
- package/package.json +1 -1
- package/polyglot/index.d.ts +1 -1
- package/polyglot/index.js +1 -1
- package/polyglot/react.d.ts +12 -2
- package/polyglot/react.js +80 -45
|
@@ -6,11 +6,13 @@ import { Tooltip } from '../../Tooltip';
|
|
|
6
6
|
import { ChipFrequencyCapping } from '../components/Condition';
|
|
7
7
|
import { IconWrapper } from '../styles';
|
|
8
8
|
import { CappingTypes } from '../types';
|
|
9
|
-
export function getReadonlyCappingLabel(cappingOption, isGlobalCappingDisabled, capping, globalCapping, isInJourney) {
|
|
10
|
-
const tooltip = getReadonlyTooltip(cappingOption, isInJourney);
|
|
9
|
+
export function getReadonlyCappingLabel(cappingOption, isGlobalCappingDisabled, capping, globalCapping, isInJourney, lang) {
|
|
10
|
+
const tooltip = getReadonlyTooltip(cappingOption, isInJourney, lang);
|
|
11
11
|
if (cappingOption === CappingTypes.CUSTOM) {
|
|
12
|
-
return React.createElement(React.Fragment, null,
|
|
13
|
-
|
|
12
|
+
return React.createElement(React.Fragment, null, lang({
|
|
13
|
+
message: 'DUMB.FREQUENCY_CAPPING.RO.CUSTOM_LABEL'
|
|
14
|
+
}), React.createElement(ChipFrequencyCapping, {
|
|
15
|
+
text: getCappingChipText(lang, capping.messages, capping.days),
|
|
14
16
|
tooltip: tooltip
|
|
15
17
|
}));
|
|
16
18
|
}
|
|
@@ -19,7 +21,9 @@ export function getReadonlyCappingLabel(cappingOption, isGlobalCappingDisabled,
|
|
|
19
21
|
"$alignItems": "center",
|
|
20
22
|
"$gap": 4,
|
|
21
23
|
"$justifyContent": "start"
|
|
22
|
-
},
|
|
24
|
+
}, lang({
|
|
25
|
+
message: 'DUMB.FREQUENCY_CAPPING.RO.IGNORE_LABEL'
|
|
26
|
+
}), React.createElement(Tooltip, {
|
|
23
27
|
content: tooltip,
|
|
24
28
|
position: "top-middle"
|
|
25
29
|
}, React.createElement(IconWrapper, {
|
|
@@ -29,19 +33,27 @@ export function getReadonlyCappingLabel(cappingOption, isGlobalCappingDisabled,
|
|
|
29
33
|
view: "lined"
|
|
30
34
|
}))));
|
|
31
35
|
}
|
|
32
|
-
return isGlobalCappingDisabled ? React.createElement(React.Fragment, null,
|
|
33
|
-
|
|
34
|
-
}),
|
|
35
|
-
text: getCappingChipText(
|
|
36
|
+
return isGlobalCappingDisabled ? React.createElement(React.Fragment, null, lang({
|
|
37
|
+
message: 'DUMB.FREQUENCY_CAPPING.RO.GLOBAL_LABEL'
|
|
38
|
+
}), React.createElement(ChipFrequencyCapping, {
|
|
39
|
+
text: getCappingChipText(lang, 0, 0)
|
|
40
|
+
}), lang({
|
|
41
|
+
message: 'DUMB.FREQUENCY_CAPPING.RO.BECAUSE_UNSET'
|
|
42
|
+
})) : React.createElement(React.Fragment, null, lang({
|
|
43
|
+
message: 'DUMB.FREQUENCY_CAPPING.RO.GLOBAL_LABEL'
|
|
44
|
+
}), React.createElement(ChipFrequencyCapping, {
|
|
45
|
+
text: getCappingChipText(lang, globalCapping.count, globalCapping.days),
|
|
36
46
|
tooltip: tooltip
|
|
37
47
|
}));
|
|
38
48
|
}
|
|
39
|
-
function getReadonlyTooltip(cappingOption, isInJourney) {
|
|
40
|
-
if (cappingOption === CappingTypes.CUSTOM) {
|
|
41
|
-
return isInJourney ? 'If the subscriber exceeds the limit of received messages, they will not receive this message and will proceed to the next step' : 'If the subscriber exceeds the limit of received messages, they will not receive this message';
|
|
42
|
-
}
|
|
49
|
+
function getReadonlyTooltip(cappingOption, isInJourney, lang) {
|
|
43
50
|
if (cappingOption === CappingTypes.IGNORE) {
|
|
44
|
-
return
|
|
51
|
+
return lang({
|
|
52
|
+
message: 'DUMB.FREQUENCY_CAPPING.RO.TOOLTIP_IGNORE'
|
|
53
|
+
});
|
|
45
54
|
}
|
|
46
|
-
return
|
|
55
|
+
return lang({
|
|
56
|
+
message: 'DUMB.FREQUENCY_CAPPING.RO.TOOLTIP_LIMIT',
|
|
57
|
+
isInJourney
|
|
58
|
+
});
|
|
47
59
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type FrequencyCappingMessages } from './types';
|
|
2
|
+
import { type LangProps } from '../../polyglot';
|
|
3
|
+
export declare const Lang: import("react").FC<LangProps<FrequencyCappingMessages>>, useLang: () => (props: LangProps<FrequencyCappingMessages>) => string;
|
|
4
|
+
export type FrequencyCappingLang = (props: LangProps<FrequencyCappingMessages>) => string;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export const messages = {
|
|
2
|
+
'DUMB.FREQUENCY_CAPPING.FIELD_TITLE': 'Frequency capping',
|
|
3
|
+
'DUMB.FREQUENCY_CAPPING.LINK.SET_UP': 'Set up',
|
|
4
|
+
'DUMB.FREQUENCY_CAPPING.LINK.SETTINGS': 'Settings',
|
|
5
|
+
'DUMB.FREQUENCY_CAPPING.SETTINGS.LIMIT_TO_MAX': 'Limit to a maximum of',
|
|
6
|
+
'DUMB.FREQUENCY_CAPPING.SETTINGS.MESSAGES_PER': 'messages per',
|
|
7
|
+
'DUMB.FREQUENCY_CAPPING.SETTINGS.DAYS': 'day(s)',
|
|
8
|
+
'DUMB.FREQUENCY_CAPPING.INAPP.LIMIT_DESC': 'Limit frequency of displaying this In-App to a particular user',
|
|
9
|
+
'DUMB.FREQUENCY_CAPPING.INAPP.LIMITED_TO': 'Limited to',
|
|
10
|
+
'DUMB.FREQUENCY_CAPPING.INAPP.TOTAL_IMPRESSIONS': 'total impressions',
|
|
11
|
+
'DUMB.FREQUENCY_CAPPING.INAPP.IMPRESSIONS_IN': 'impressions in',
|
|
12
|
+
'DUMB.FREQUENCY_CAPPING.INAPP.DAYS': 'day(s)',
|
|
13
|
+
'DUMB.FREQUENCY_CAPPING.INAPP.ENABLE': 'Enable',
|
|
14
|
+
'DUMB.FREQUENCY_CAPPING.INAPP.DAYS_INTERVAL': 'day(s) interval between impressions',
|
|
15
|
+
'DUMB.FREQUENCY_CAPPING.CHIP.UNLIMITED': 'unlimited',
|
|
16
|
+
'DUMB.FREQUENCY_CAPPING.CHIP.MESSAGE': '{{count}} {{count | pluralize: "message", "messages"}} per {{days}} {{days | pluralize: "day", "days"}}',
|
|
17
|
+
'DUMB.FREQUENCY_CAPPING.CHIP.INAPP': '{{count}} {{count | pluralize: "in-app", "in-apps"}} per {{days}} {{days | pluralize: "day", "days"}}',
|
|
18
|
+
'DUMB.FREQUENCY_CAPPING.DESC.GLOBAL_INAPP_UNSET': 'This in-app will use a global frequency capping because limit isn’t set.',
|
|
19
|
+
'DUMB.FREQUENCY_CAPPING.DESC.GLOBAL_PREFIX': 'Under the current setup, If the user receives more than',
|
|
20
|
+
'DUMB.FREQUENCY_CAPPING.DESC.GLOBAL_INAPP_SUFFIX': 'this in-app will not be delivered.',
|
|
21
|
+
'DUMB.FREQUENCY_CAPPING.DESC.GLOBAL_MSG_UNSET_PREFIX': 'This message will use an',
|
|
22
|
+
'DUMB.FREQUENCY_CAPPING.DESC.GLOBAL_MSG_UNSET_SUFFIX': 'global frequency capping because limit isn’t set.',
|
|
23
|
+
'DUMB.FREQUENCY_CAPPING.DESC.GLOBAL_MSG_SUFFIX': ', this message will not be delivered{% if isInJourney == true %}, and the user will proceed to the next step{% endif %}.',
|
|
24
|
+
'DUMB.FREQUENCY_CAPPING.DESC.CUSTOM_INAPP': 'Instead of Global frequency capping, the limit of shown in-apps you set will be used. If the subscriber exceeds the limit of received in-apps, they will not see the in-app again.',
|
|
25
|
+
'DUMB.FREQUENCY_CAPPING.DESC.CUSTOM_MSG': 'Instead of Global frequency capping, the limit of sent messages you set will be used. If the subscriber exceeds the limit of received messages, they will not receive this message{% if isInJourney == true %}, and will proceed to the next step{% endif %}.',
|
|
26
|
+
'DUMB.FREQUENCY_CAPPING.DESC.IGNORE': 'The user will receive this message regardless, even if they have exceeded the set message limits for the channel. Use with caution to avoid sending too many messages.',
|
|
27
|
+
'DUMB.FREQUENCY_CAPPING.RO.CUSTOM_LABEL': 'Custom frequency capping for this message:',
|
|
28
|
+
'DUMB.FREQUENCY_CAPPING.RO.IGNORE_LABEL': 'Send messages without global frequency capping',
|
|
29
|
+
'DUMB.FREQUENCY_CAPPING.RO.GLOBAL_LABEL': 'Global frequency capping:',
|
|
30
|
+
'DUMB.FREQUENCY_CAPPING.RO.BECAUSE_UNSET': 'because limit isn’t set',
|
|
31
|
+
'DUMB.FREQUENCY_CAPPING.RO.TOOLTIP_LIMIT': 'If the subscriber exceeds the limit of received messages, they will not receive this message{% if isInJourney == true %} and will proceed to the next step{% endif %}',
|
|
32
|
+
'DUMB.FREQUENCY_CAPPING.RO.TOOLTIP_IGNORE': 'The user will receive this message regardless, even if they have exceeded the set limits for the channel'
|
|
33
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export const messages = {
|
|
2
|
+
'DUMB.FREQUENCY_CAPPING.FIELD_TITLE': 'Ограничение частоты',
|
|
3
|
+
'DUMB.FREQUENCY_CAPPING.LINK.SET_UP': 'Настроить',
|
|
4
|
+
'DUMB.FREQUENCY_CAPPING.LINK.SETTINGS': 'Настройки',
|
|
5
|
+
'DUMB.FREQUENCY_CAPPING.SETTINGS.LIMIT_TO_MAX': 'Не более',
|
|
6
|
+
'DUMB.FREQUENCY_CAPPING.SETTINGS.MESSAGES_PER': 'сообщений за',
|
|
7
|
+
'DUMB.FREQUENCY_CAPPING.SETTINGS.DAYS': 'дн.',
|
|
8
|
+
'DUMB.FREQUENCY_CAPPING.INAPP.LIMIT_DESC': 'Ограничьте частоту показа этого In-App конкретному пользователю',
|
|
9
|
+
'DUMB.FREQUENCY_CAPPING.INAPP.LIMITED_TO': 'Не более',
|
|
10
|
+
'DUMB.FREQUENCY_CAPPING.INAPP.TOTAL_IMPRESSIONS': 'показов всего',
|
|
11
|
+
'DUMB.FREQUENCY_CAPPING.INAPP.IMPRESSIONS_IN': 'показов за',
|
|
12
|
+
'DUMB.FREQUENCY_CAPPING.INAPP.DAYS': 'дн.',
|
|
13
|
+
'DUMB.FREQUENCY_CAPPING.INAPP.ENABLE': 'Включить',
|
|
14
|
+
'DUMB.FREQUENCY_CAPPING.INAPP.DAYS_INTERVAL': 'дн. интервал между показами',
|
|
15
|
+
'DUMB.FREQUENCY_CAPPING.CHIP.UNLIMITED': 'без ограничений',
|
|
16
|
+
'DUMB.FREQUENCY_CAPPING.CHIP.MESSAGE': '{{count}} {{count | pluralize: "сообщение", "сообщения", "сообщений"}} за {{days}} {{days | pluralize: "день", "дня", "дней"}}',
|
|
17
|
+
'DUMB.FREQUENCY_CAPPING.CHIP.INAPP': '{{count}} in-app за {{days}} {{days | pluralize: "день", "дня", "дней"}}',
|
|
18
|
+
'DUMB.FREQUENCY_CAPPING.DESC.GLOBAL_INAPP_UNSET': 'Этот in-app будет использовать глобальное ограничение частоты, так как лимит не задан.',
|
|
19
|
+
'DUMB.FREQUENCY_CAPPING.DESC.GLOBAL_PREFIX': 'При текущих настройках, если пользователь получит более',
|
|
20
|
+
'DUMB.FREQUENCY_CAPPING.DESC.GLOBAL_INAPP_SUFFIX': 'этот in-app не будет доставлен.',
|
|
21
|
+
'DUMB.FREQUENCY_CAPPING.DESC.GLOBAL_MSG_UNSET_PREFIX': 'Это сообщение будет использовать',
|
|
22
|
+
'DUMB.FREQUENCY_CAPPING.DESC.GLOBAL_MSG_UNSET_SUFFIX': 'глобальное ограничение частоты, так как лимит не задан.',
|
|
23
|
+
'DUMB.FREQUENCY_CAPPING.DESC.GLOBAL_MSG_SUFFIX': ', это сообщение не будет доставлено{% if isInJourney == true %}, а пользователь перейдёт к следующему шагу{% endif %}.',
|
|
24
|
+
'DUMB.FREQUENCY_CAPPING.DESC.CUSTOM_INAPP': 'Вместо глобального ограничения частоты будет использован заданный вами лимит показов in-app. Если подписчик превысит лимит полученных in-app, он больше не увидит этот in-app.',
|
|
25
|
+
'DUMB.FREQUENCY_CAPPING.DESC.CUSTOM_MSG': 'Вместо глобального ограничения частоты будет использован заданный вами лимит отправленных сообщений. Если подписчик превысит лимит полученных сообщений, он не получит это сообщение{% if isInJourney == true %} и перейдёт к следующему шагу{% endif %}.',
|
|
26
|
+
'DUMB.FREQUENCY_CAPPING.DESC.IGNORE': 'Пользователь получит это сообщение в любом случае, даже если он превысил установленные лимиты сообщений для канала. Используйте с осторожностью, чтобы не отправлять слишком много сообщений.',
|
|
27
|
+
'DUMB.FREQUENCY_CAPPING.RO.CUSTOM_LABEL': 'Пользовательское ограничение частоты для этого сообщения:',
|
|
28
|
+
'DUMB.FREQUENCY_CAPPING.RO.IGNORE_LABEL': 'Отправлять сообщения без глобального ограничения частоты',
|
|
29
|
+
'DUMB.FREQUENCY_CAPPING.RO.GLOBAL_LABEL': 'Глобальное ограничение частоты:',
|
|
30
|
+
'DUMB.FREQUENCY_CAPPING.RO.BECAUSE_UNSET': 'так как лимит не задан',
|
|
31
|
+
'DUMB.FREQUENCY_CAPPING.RO.TOOLTIP_LIMIT': 'Если подписчик превысит лимит полученных сообщений, он не получит это сообщение{% if isInJourney == true %} и перейдёт к следующему шагу{% endif %}',
|
|
32
|
+
'DUMB.FREQUENCY_CAPPING.RO.TOOLTIP_IGNORE': 'Пользователь получит это сообщение в любом случае, даже если он превысил установленные лимиты для канала'
|
|
33
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { NoParams } from '../../polyglot';
|
|
2
|
+
export type FrequencyCappingMessages = {
|
|
3
|
+
'DUMB.FREQUENCY_CAPPING.FIELD_TITLE': NoParams;
|
|
4
|
+
'DUMB.FREQUENCY_CAPPING.LINK.SET_UP': NoParams;
|
|
5
|
+
'DUMB.FREQUENCY_CAPPING.LINK.SETTINGS': NoParams;
|
|
6
|
+
'DUMB.FREQUENCY_CAPPING.SETTINGS.LIMIT_TO_MAX': NoParams;
|
|
7
|
+
'DUMB.FREQUENCY_CAPPING.SETTINGS.MESSAGES_PER': NoParams;
|
|
8
|
+
'DUMB.FREQUENCY_CAPPING.SETTINGS.DAYS': NoParams;
|
|
9
|
+
'DUMB.FREQUENCY_CAPPING.INAPP.LIMIT_DESC': NoParams;
|
|
10
|
+
'DUMB.FREQUENCY_CAPPING.INAPP.LIMITED_TO': NoParams;
|
|
11
|
+
'DUMB.FREQUENCY_CAPPING.INAPP.TOTAL_IMPRESSIONS': NoParams;
|
|
12
|
+
'DUMB.FREQUENCY_CAPPING.INAPP.IMPRESSIONS_IN': NoParams;
|
|
13
|
+
'DUMB.FREQUENCY_CAPPING.INAPP.DAYS': NoParams;
|
|
14
|
+
'DUMB.FREQUENCY_CAPPING.INAPP.ENABLE': NoParams;
|
|
15
|
+
'DUMB.FREQUENCY_CAPPING.INAPP.DAYS_INTERVAL': NoParams;
|
|
16
|
+
'DUMB.FREQUENCY_CAPPING.CHIP.UNLIMITED': NoParams;
|
|
17
|
+
'DUMB.FREQUENCY_CAPPING.CHIP.MESSAGE': {
|
|
18
|
+
count: number;
|
|
19
|
+
days: number;
|
|
20
|
+
};
|
|
21
|
+
'DUMB.FREQUENCY_CAPPING.CHIP.INAPP': {
|
|
22
|
+
count: number;
|
|
23
|
+
days: number;
|
|
24
|
+
};
|
|
25
|
+
'DUMB.FREQUENCY_CAPPING.DESC.GLOBAL_INAPP_UNSET': NoParams;
|
|
26
|
+
'DUMB.FREQUENCY_CAPPING.DESC.GLOBAL_PREFIX': NoParams;
|
|
27
|
+
'DUMB.FREQUENCY_CAPPING.DESC.GLOBAL_INAPP_SUFFIX': NoParams;
|
|
28
|
+
'DUMB.FREQUENCY_CAPPING.DESC.GLOBAL_MSG_UNSET_PREFIX': NoParams;
|
|
29
|
+
'DUMB.FREQUENCY_CAPPING.DESC.GLOBAL_MSG_UNSET_SUFFIX': NoParams;
|
|
30
|
+
'DUMB.FREQUENCY_CAPPING.DESC.GLOBAL_MSG_SUFFIX': {
|
|
31
|
+
isInJourney: boolean;
|
|
32
|
+
};
|
|
33
|
+
'DUMB.FREQUENCY_CAPPING.DESC.CUSTOM_INAPP': NoParams;
|
|
34
|
+
'DUMB.FREQUENCY_CAPPING.DESC.CUSTOM_MSG': {
|
|
35
|
+
isInJourney: boolean;
|
|
36
|
+
};
|
|
37
|
+
'DUMB.FREQUENCY_CAPPING.DESC.IGNORE': NoParams;
|
|
38
|
+
'DUMB.FREQUENCY_CAPPING.RO.CUSTOM_LABEL': NoParams;
|
|
39
|
+
'DUMB.FREQUENCY_CAPPING.RO.IGNORE_LABEL': NoParams;
|
|
40
|
+
'DUMB.FREQUENCY_CAPPING.RO.GLOBAL_LABEL': NoParams;
|
|
41
|
+
'DUMB.FREQUENCY_CAPPING.RO.BECAUSE_UNSET': NoParams;
|
|
42
|
+
'DUMB.FREQUENCY_CAPPING.RO.TOOLTIP_LIMIT': {
|
|
43
|
+
isInJourney: boolean;
|
|
44
|
+
};
|
|
45
|
+
'DUMB.FREQUENCY_CAPPING.RO.TOOLTIP_IGNORE': NoParams;
|
|
46
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/SendRate/SendRateEdit.js
CHANGED
|
@@ -4,10 +4,12 @@ import { Vertical } from '@pushwoosh/kit-helpers';
|
|
|
4
4
|
import { OpenIcon } from '@pushwoosh/kit-icons';
|
|
5
5
|
import { Description } from './components/Description';
|
|
6
6
|
import { EditSendRateSettings } from './components/Settings';
|
|
7
|
+
import { getGlobalSendRateLinkText } from './helpers/getGlobalSendRateLinkText';
|
|
7
8
|
import { FieldBox } from '../FieldBox';
|
|
8
9
|
import { Link } from '../Link';
|
|
9
10
|
import { Select } from '../Select';
|
|
10
11
|
import { sendRateOptions } from './constants';
|
|
12
|
+
import { useLang } from './polyglot';
|
|
11
13
|
import { Container, LinkWrapper } from './styles';
|
|
12
14
|
export function SendRateEdit({
|
|
13
15
|
sendRate,
|
|
@@ -18,12 +20,13 @@ export function SendRateEdit({
|
|
|
18
20
|
maxValue,
|
|
19
21
|
editGlobalClicked
|
|
20
22
|
}) {
|
|
23
|
+
const lang = useLang();
|
|
21
24
|
const {
|
|
22
25
|
sendRate: count,
|
|
23
26
|
policy
|
|
24
27
|
} = sendRate;
|
|
25
28
|
const isGlobalSendRateDisabled = globalSendRate.count === 0 && globalSendRate.count === 0;
|
|
26
|
-
const globalSendRateLinkText = isGlobalSendRateDisabled
|
|
29
|
+
const globalSendRateLinkText = getGlobalSendRateLinkText(lang, isGlobalSendRateDisabled);
|
|
27
30
|
const onChangeSendRate = React.useCallback(value => {
|
|
28
31
|
if (value === 'CUSTOM') {
|
|
29
32
|
onChange({
|
|
@@ -51,7 +54,9 @@ export function SendRateEdit({
|
|
|
51
54
|
}, React.createElement(Vertical, {
|
|
52
55
|
"$gap": 12
|
|
53
56
|
}, React.createElement(FieldBox, {
|
|
54
|
-
title:
|
|
57
|
+
title: lang({
|
|
58
|
+
message: 'DUMB.SEND_RATE.FIELD_TITLE'
|
|
59
|
+
})
|
|
55
60
|
}, React.createElement(Select, {
|
|
56
61
|
name: "sendRateOption",
|
|
57
62
|
options: sendRateOptions,
|
|
@@ -2,19 +2,22 @@ import React from 'react';
|
|
|
2
2
|
import { Horizontal, Vertical } from '@pushwoosh/kit-helpers';
|
|
3
3
|
import { OpenIcon } from '@pushwoosh/kit-icons';
|
|
4
4
|
import { Link } from '../Link';
|
|
5
|
+
import { getGlobalSendRateLinkText } from './helpers/getGlobalSendRateLinkText';
|
|
5
6
|
import { getReadonlySendRateLabel } from './helpers/getReadonlySendRateHelpers';
|
|
7
|
+
import { useLang } from './polyglot';
|
|
6
8
|
export function SendRateReadonly({
|
|
7
9
|
sendRate,
|
|
8
10
|
globalSendRate,
|
|
9
11
|
applicationCode,
|
|
10
12
|
editGlobalClicked
|
|
11
13
|
}) {
|
|
14
|
+
const lang = useLang();
|
|
12
15
|
const {
|
|
13
16
|
policy
|
|
14
17
|
} = sendRate;
|
|
15
18
|
const isGlobalSendRateDisabled = globalSendRate.count === 0 && globalSendRate.seconds === 0;
|
|
16
|
-
const globalSendRateLinkText = isGlobalSendRateDisabled
|
|
17
|
-
const label = getReadonlySendRateLabel(sendRate, isGlobalSendRateDisabled, globalSendRate);
|
|
19
|
+
const globalSendRateLinkText = getGlobalSendRateLinkText(lang, isGlobalSendRateDisabled);
|
|
20
|
+
const label = getReadonlySendRateLabel(sendRate, isGlobalSendRateDisabled, globalSendRate, lang);
|
|
18
21
|
return React.createElement(Vertical, {
|
|
19
22
|
"$gap": 24
|
|
20
23
|
}, React.createElement(Horizontal, {
|
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { ChipWithinTextContainer } from './styles';
|
|
3
3
|
import { Chip } from '../../../Chip';
|
|
4
|
+
import { useLang } from '../../polyglot';
|
|
4
5
|
export function ChipSendRate({
|
|
5
6
|
messages,
|
|
6
7
|
$inText = false
|
|
7
8
|
}) {
|
|
9
|
+
const lang = useLang();
|
|
8
10
|
return React.createElement(ChipWithinTextContainer, {
|
|
9
11
|
"$inText": $inText
|
|
10
12
|
}, React.createElement(Chip, {
|
|
11
13
|
"$variant": "primary",
|
|
12
14
|
"$readOnly": true,
|
|
13
15
|
as: "span"
|
|
14
|
-
}, messages === 0 ?
|
|
16
|
+
}, messages === 0 ? lang({
|
|
17
|
+
message: 'DUMB.SEND_RATE.CHIP.UNLIMITED'
|
|
18
|
+
}) : lang({
|
|
19
|
+
message: 'DUMB.SEND_RATE.CHIP.RATE',
|
|
20
|
+
messages
|
|
21
|
+
})));
|
|
15
22
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Paragraph } from '@pushwoosh/kit-typography';
|
|
3
|
+
import { useLang } from '../../polyglot';
|
|
3
4
|
import { ChipSendRate } from '../Condition';
|
|
4
5
|
export function Description(props) {
|
|
5
6
|
const {
|
|
@@ -8,22 +9,35 @@ export function Description(props) {
|
|
|
8
9
|
seconds,
|
|
9
10
|
children
|
|
10
11
|
} = props;
|
|
12
|
+
const lang = useLang();
|
|
11
13
|
if (sendRateOption === 'GLOBAL') {
|
|
12
14
|
if (messages === 0 && seconds === 0) {
|
|
13
|
-
return React.createElement(Paragraph, null,
|
|
15
|
+
return React.createElement(Paragraph, null, lang({
|
|
16
|
+
message: 'DUMB.SEND_RATE.DESC.GLOBAL_UNSET_PREFIX'
|
|
17
|
+
}), React.createElement(ChipSendRate, {
|
|
14
18
|
messages: 0,
|
|
15
19
|
"$inText": true
|
|
16
|
-
}),
|
|
20
|
+
}), lang({
|
|
21
|
+
message: 'DUMB.SEND_RATE.DESC.GLOBAL_UNSET_SUFFIX'
|
|
22
|
+
}), ' ', children);
|
|
17
23
|
}
|
|
18
|
-
return React.createElement(Paragraph, null,
|
|
24
|
+
return React.createElement(Paragraph, null, lang({
|
|
25
|
+
message: 'DUMB.SEND_RATE.DESC.GLOBAL_SET_PREFIX'
|
|
26
|
+
}), React.createElement(ChipSendRate, {
|
|
19
27
|
messages: messages,
|
|
20
28
|
"$inText": true
|
|
21
|
-
}),
|
|
29
|
+
}), lang({
|
|
30
|
+
message: 'DUMB.SEND_RATE.DESC.GLOBAL_SET_SUFFIX'
|
|
31
|
+
}), ' ', children);
|
|
22
32
|
}
|
|
23
33
|
if (sendRateOption === 'CUSTOM') {
|
|
24
|
-
return React.createElement(Paragraph, null,
|
|
34
|
+
return React.createElement(Paragraph, null, lang({
|
|
35
|
+
message: 'DUMB.SEND_RATE.DESC.CUSTOM'
|
|
36
|
+
}), ' ', children);
|
|
25
37
|
}
|
|
26
38
|
if (sendRateOption === 'IGNORE') {
|
|
27
|
-
return React.createElement(Paragraph, null,
|
|
39
|
+
return React.createElement(Paragraph, null, lang({
|
|
40
|
+
message: 'DUMB.SEND_RATE.DESC.IGNORE'
|
|
41
|
+
}), ' ', children);
|
|
28
42
|
}
|
|
29
43
|
}
|
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { Horizontal } from '@pushwoosh/kit-helpers';
|
|
3
3
|
import { Paragraph } from '@pushwoosh/kit-typography';
|
|
4
4
|
import { NumberPicker } from '../../../NumberPicker';
|
|
5
|
+
import { Lang } from '../../polyglot';
|
|
5
6
|
export function EditSendRateSettings(props) {
|
|
6
7
|
const {
|
|
7
8
|
messages,
|
|
@@ -18,10 +19,14 @@ export function EditSendRateSettings(props) {
|
|
|
18
19
|
style: {
|
|
19
20
|
gridTemplateColumns: '145px 70px 150px'
|
|
20
21
|
}
|
|
21
|
-
}, React.createElement(Paragraph, null,
|
|
22
|
+
}, React.createElement(Paragraph, null, React.createElement(Lang, {
|
|
23
|
+
message: "DUMB.SEND_RATE.SETTINGS.LIMIT_TO_MAX"
|
|
24
|
+
})), React.createElement(NumberPicker, {
|
|
22
25
|
onChange: value => onMessagesChange(value),
|
|
23
26
|
value: messages,
|
|
24
27
|
minValue: minValue,
|
|
25
28
|
maxValue: maxValue
|
|
26
|
-
}), React.createElement(Paragraph, null,
|
|
29
|
+
}), React.createElement(Paragraph, null, React.createElement(Lang, {
|
|
30
|
+
message: "DUMB.SEND_RATE.SETTINGS.MESSAGES_PER_SEC"
|
|
31
|
+
})));
|
|
27
32
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { type SendRateLang } from '../polyglot';
|
|
2
3
|
import { type SendRateBase } from '../types';
|
|
3
4
|
export declare function getReadonlySendRateLabel<T extends SendRateBase>(sendRateOption: T, isGlobalSendRateDisabled: boolean, globalSendRate: {
|
|
4
5
|
count: number;
|
|
5
6
|
seconds: number;
|
|
6
|
-
}): React.JSX.Element;
|
|
7
|
+
}, lang: SendRateLang): React.JSX.Element;
|
|
@@ -4,14 +4,16 @@ import { HelpRoundIcon } from '@pushwoosh/kit-icons';
|
|
|
4
4
|
import { Tooltip } from '../../Tooltip';
|
|
5
5
|
import { ChipSendRate } from '../components/Condition';
|
|
6
6
|
import { IconWrapper } from '../styles';
|
|
7
|
-
export function getReadonlySendRateLabel(sendRateOption, isGlobalSendRateDisabled, globalSendRate) {
|
|
7
|
+
export function getReadonlySendRateLabel(sendRateOption, isGlobalSendRateDisabled, globalSendRate, lang) {
|
|
8
8
|
const {
|
|
9
9
|
policy,
|
|
10
10
|
sendRate
|
|
11
11
|
} = sendRateOption;
|
|
12
|
-
const tooltip = getReadonlyTooltip(policy);
|
|
12
|
+
const tooltip = getReadonlyTooltip(policy, lang);
|
|
13
13
|
if (policy === 'CUSTOM') {
|
|
14
|
-
return React.createElement(React.Fragment, null,
|
|
14
|
+
return React.createElement(React.Fragment, null, lang({
|
|
15
|
+
message: 'DUMB.SEND_RATE.RO.CUSTOM_LABEL'
|
|
16
|
+
}), React.createElement(ChipSendRate, {
|
|
15
17
|
messages: sendRate
|
|
16
18
|
}));
|
|
17
19
|
}
|
|
@@ -20,7 +22,9 @@ export function getReadonlySendRateLabel(sendRateOption, isGlobalSendRateDisable
|
|
|
20
22
|
"$alignItems": "center",
|
|
21
23
|
"$gap": 4,
|
|
22
24
|
"$justifyContent": "start"
|
|
23
|
-
},
|
|
25
|
+
}, lang({
|
|
26
|
+
message: 'DUMB.SEND_RATE.RO.IGNORE_LABEL'
|
|
27
|
+
}), React.createElement(Tooltip, {
|
|
24
28
|
content: tooltip,
|
|
25
29
|
position: "top-middle"
|
|
26
30
|
}, React.createElement(IconWrapper, {
|
|
@@ -30,18 +34,23 @@ export function getReadonlySendRateLabel(sendRateOption, isGlobalSendRateDisable
|
|
|
30
34
|
view: "lined"
|
|
31
35
|
}))));
|
|
32
36
|
}
|
|
33
|
-
return isGlobalSendRateDisabled ? React.createElement(React.Fragment, null,
|
|
37
|
+
return isGlobalSendRateDisabled ? React.createElement(React.Fragment, null, lang({
|
|
38
|
+
message: 'DUMB.SEND_RATE.RO.GLOBAL_LABEL'
|
|
39
|
+
}), React.createElement(ChipSendRate, {
|
|
34
40
|
messages: 0
|
|
35
|
-
}),
|
|
41
|
+
}), lang({
|
|
42
|
+
message: 'DUMB.SEND_RATE.RO.BECAUSE_UNSET'
|
|
43
|
+
})) : React.createElement(React.Fragment, null, lang({
|
|
44
|
+
message: 'DUMB.SEND_RATE.RO.GLOBAL_LABEL'
|
|
45
|
+
}), React.createElement(ChipSendRate, {
|
|
36
46
|
messages: globalSendRate.count
|
|
37
47
|
}));
|
|
38
48
|
}
|
|
39
|
-
function getReadonlyTooltip(sendRateOption) {
|
|
40
|
-
if (sendRateOption === 'CUSTOM') {
|
|
41
|
-
return '';
|
|
42
|
-
}
|
|
49
|
+
function getReadonlyTooltip(sendRateOption, lang) {
|
|
43
50
|
if (sendRateOption === 'IGNORE') {
|
|
44
|
-
return
|
|
51
|
+
return lang({
|
|
52
|
+
message: 'DUMB.SEND_RATE.RO.TOOLTIP_IGNORE'
|
|
53
|
+
});
|
|
45
54
|
}
|
|
46
55
|
return '';
|
|
47
56
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type SendRateMessages } from './types';
|
|
2
|
+
import { type LangProps } from '../../polyglot';
|
|
3
|
+
export declare const Lang: import("react").FC<LangProps<SendRateMessages>>, useLang: () => (props: LangProps<SendRateMessages>) => string;
|
|
4
|
+
export type SendRateLang = (props: LangProps<SendRateMessages>) => string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const messages = {
|
|
2
|
+
'DUMB.SEND_RATE.FIELD_TITLE': 'Send rate',
|
|
3
|
+
'DUMB.SEND_RATE.LINK.SET_UP': 'Set up',
|
|
4
|
+
'DUMB.SEND_RATE.LINK.SETTINGS': 'Settings',
|
|
5
|
+
'DUMB.SEND_RATE.SETTINGS.LIMIT_TO_MAX': 'Limit to a maximum of',
|
|
6
|
+
'DUMB.SEND_RATE.SETTINGS.MESSAGES_PER_SEC': 'messages per 1 sec',
|
|
7
|
+
'DUMB.SEND_RATE.CHIP.UNLIMITED': 'unlimited',
|
|
8
|
+
'DUMB.SEND_RATE.CHIP.RATE': '{{messages}} messages per 1 sec',
|
|
9
|
+
'DUMB.SEND_RATE.DESC.GLOBAL_UNSET_PREFIX': 'This message will use an',
|
|
10
|
+
'DUMB.SEND_RATE.DESC.GLOBAL_UNSET_SUFFIX': 'global send rate because limit isn’t set.',
|
|
11
|
+
'DUMB.SEND_RATE.DESC.GLOBAL_SET_PREFIX': 'This message will use the Global send rate limit of',
|
|
12
|
+
'DUMB.SEND_RATE.DESC.GLOBAL_SET_SUFFIX': 'to help reduce backend load.',
|
|
13
|
+
'DUMB.SEND_RATE.DESC.CUSTOM': 'Instead of using the Global send rate, a custom delivery speed will be applied. Messages will be sent at the rate defined below.',
|
|
14
|
+
'DUMB.SEND_RATE.DESC.IGNORE': 'This message will be sent as fast as possible, regardless of any Global send rate limits. Use with caution to avoid overloading your backend or creating delivery spikes.',
|
|
15
|
+
'DUMB.SEND_RATE.RO.CUSTOM_LABEL': 'Custom send rate for this message:',
|
|
16
|
+
'DUMB.SEND_RATE.RO.IGNORE_LABEL': 'Send messages without global send rate',
|
|
17
|
+
'DUMB.SEND_RATE.RO.GLOBAL_LABEL': 'Global send rate:',
|
|
18
|
+
'DUMB.SEND_RATE.RO.BECAUSE_UNSET': 'because limit isn’t set',
|
|
19
|
+
'DUMB.SEND_RATE.RO.TOOLTIP_IGNORE': 'Messages will be sent without rate limits, if possible, because no send rate is applied to this message'
|
|
20
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const messages = {
|
|
2
|
+
'DUMB.SEND_RATE.FIELD_TITLE': 'Скорость отправки',
|
|
3
|
+
'DUMB.SEND_RATE.LINK.SET_UP': 'Настроить',
|
|
4
|
+
'DUMB.SEND_RATE.LINK.SETTINGS': 'Настройки',
|
|
5
|
+
'DUMB.SEND_RATE.SETTINGS.LIMIT_TO_MAX': 'Не более',
|
|
6
|
+
'DUMB.SEND_RATE.SETTINGS.MESSAGES_PER_SEC': 'сообщений в секунду',
|
|
7
|
+
'DUMB.SEND_RATE.CHIP.UNLIMITED': 'без ограничений',
|
|
8
|
+
'DUMB.SEND_RATE.CHIP.RATE': '{{messages}} {{messages | pluralize: "сообщение", "сообщения", "сообщений"}} в секунду',
|
|
9
|
+
'DUMB.SEND_RATE.DESC.GLOBAL_UNSET_PREFIX': 'Это сообщение будет использовать',
|
|
10
|
+
'DUMB.SEND_RATE.DESC.GLOBAL_UNSET_SUFFIX': 'глобальную скорость отправки, так как лимит не задан.',
|
|
11
|
+
'DUMB.SEND_RATE.DESC.GLOBAL_SET_PREFIX': 'Это сообщение будет использовать глобальный лимит скорости отправки',
|
|
12
|
+
'DUMB.SEND_RATE.DESC.GLOBAL_SET_SUFFIX': 'чтобы снизить нагрузку на бэкенд.',
|
|
13
|
+
'DUMB.SEND_RATE.DESC.CUSTOM': 'Вместо глобальной скорости отправки будет применена пользовательская скорость доставки. Сообщения будут отправляться со скоростью, заданной ниже.',
|
|
14
|
+
'DUMB.SEND_RATE.DESC.IGNORE': 'Это сообщение будет отправлено максимально быстро, без учёта глобальных лимитов скорости отправки. Используйте с осторожностью, чтобы не перегрузить бэкенд и не создавать всплески доставки.',
|
|
15
|
+
'DUMB.SEND_RATE.RO.CUSTOM_LABEL': 'Пользовательская скорость отправки для этого сообщения:',
|
|
16
|
+
'DUMB.SEND_RATE.RO.IGNORE_LABEL': 'Отправлять сообщения без глобальной скорости отправки',
|
|
17
|
+
'DUMB.SEND_RATE.RO.GLOBAL_LABEL': 'Глобальная скорость отправки:',
|
|
18
|
+
'DUMB.SEND_RATE.RO.BECAUSE_UNSET': 'так как лимит не задан',
|
|
19
|
+
'DUMB.SEND_RATE.RO.TOOLTIP_IGNORE': 'Сообщения будут отправлены без ограничений скорости, по возможности, так как к этому сообщению не применяется скорость отправки'
|
|
20
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { NoParams } from '../../polyglot';
|
|
2
|
+
export type SendRateMessages = {
|
|
3
|
+
'DUMB.SEND_RATE.FIELD_TITLE': NoParams;
|
|
4
|
+
'DUMB.SEND_RATE.LINK.SET_UP': NoParams;
|
|
5
|
+
'DUMB.SEND_RATE.LINK.SETTINGS': NoParams;
|
|
6
|
+
'DUMB.SEND_RATE.SETTINGS.LIMIT_TO_MAX': NoParams;
|
|
7
|
+
'DUMB.SEND_RATE.SETTINGS.MESSAGES_PER_SEC': NoParams;
|
|
8
|
+
'DUMB.SEND_RATE.CHIP.UNLIMITED': NoParams;
|
|
9
|
+
'DUMB.SEND_RATE.CHIP.RATE': {
|
|
10
|
+
messages: number;
|
|
11
|
+
};
|
|
12
|
+
'DUMB.SEND_RATE.DESC.GLOBAL_UNSET_PREFIX': NoParams;
|
|
13
|
+
'DUMB.SEND_RATE.DESC.GLOBAL_UNSET_SUFFIX': NoParams;
|
|
14
|
+
'DUMB.SEND_RATE.DESC.GLOBAL_SET_PREFIX': NoParams;
|
|
15
|
+
'DUMB.SEND_RATE.DESC.GLOBAL_SET_SUFFIX': NoParams;
|
|
16
|
+
'DUMB.SEND_RATE.DESC.CUSTOM': NoParams;
|
|
17
|
+
'DUMB.SEND_RATE.DESC.IGNORE': NoParams;
|
|
18
|
+
'DUMB.SEND_RATE.RO.CUSTOM_LABEL': NoParams;
|
|
19
|
+
'DUMB.SEND_RATE.RO.IGNORE_LABEL': NoParams;
|
|
20
|
+
'DUMB.SEND_RATE.RO.GLOBAL_LABEL': NoParams;
|
|
21
|
+
'DUMB.SEND_RATE.RO.BECAUSE_UNSET': NoParams;
|
|
22
|
+
'DUMB.SEND_RATE.RO.TOOLTIP_IGNORE': NoParams;
|
|
23
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
package/polyglot/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export type { LangMessages, LangProps, UseLangHook, NoParams } from './types';
|
|
2
|
-
export { LangProvider, LangBase, useLangBase } from './react';
|
|
2
|
+
export { LangProvider, LangBase, useLangBase, LangLib, useLangLib, createLang, } from './react';
|
package/polyglot/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { LangProvider, LangBase, useLangBase } from './react';
|
|
1
|
+
export { LangProvider, LangBase, useLangBase, LangLib, useLangLib, createLang } from './react';
|
package/polyglot/react.d.ts
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import { type FC, type PropsWithChildren } from 'react';
|
|
2
|
-
import { type LangMessages } from './types';
|
|
2
|
+
import { type LangMessages, type LangProps } from './types';
|
|
3
|
+
type LanguagesMap = Record<string, LangMessages<any>>;
|
|
3
4
|
type LangProviderProps = {
|
|
4
5
|
timezone: string;
|
|
5
6
|
language: string;
|
|
6
|
-
languages:
|
|
7
|
+
languages: LanguagesMap;
|
|
7
8
|
};
|
|
8
9
|
export declare const LangProvider: FC<PropsWithChildren<LangProviderProps>>;
|
|
9
10
|
export declare const LangBase: FC;
|
|
10
11
|
export declare const useLangBase: () => (allProps: any) => string;
|
|
12
|
+
type LangLibProps = {
|
|
13
|
+
$languages: LanguagesMap;
|
|
14
|
+
} & Record<string, any>;
|
|
15
|
+
export declare const LangLib: FC<LangLibProps>;
|
|
16
|
+
export declare const useLangLib: (languages: LanguagesMap) => (allProps: any) => string;
|
|
17
|
+
export declare const createLang: <M>(languages: Record<string, LangMessages<M>>) => {
|
|
18
|
+
Lang: FC<LangProps<M>>;
|
|
19
|
+
useLang: () => (props: LangProps<M>) => string;
|
|
20
|
+
};
|
|
11
21
|
export {};
|