@floristcloud/api-lib 1.2.112 → 1.2.114
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/build/constant/contact-phone.constant.js +21 -0
- package/build/constant/error.js +3 -0
- package/build/constant/index.js +2 -0
- package/build/constant/settings.constant.js +6 -0
- package/build/constant/validation-i18n-key.js +2 -0
- package/build/constant/validation-i18n-message.js +2 -0
- package/build/enum/contact-messenger.enum.js +9 -0
- package/build/enum/index.js +1 -0
- package/build/schemas/settings/settings.schema.js +51 -1
- package/constant/contact-phone.constant.ts +22 -0
- package/constant/error.ts +3 -0
- package/constant/index.ts +2 -0
- package/constant/settings.constant.ts +3 -0
- package/constant/validation-i18n-key.ts +2 -0
- package/constant/validation-i18n-message.ts +2 -0
- package/enum/contact-messenger.enum.ts +5 -0
- package/enum/index.ts +1 -0
- package/package.json +1 -1
- package/schemas/settings/settings.schema.ts +70 -2
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MESSENGERS_REQUIRING_URL = exports.CONTACT_MESSENGER_URL_MAX_LENGTH = exports.CONTACT_PHONE_LABEL_MAX_LENGTH = exports.CONTACT_PHONE_MAX_LENGTH = exports.CONTACT_PHONE_LIST_MAX_LENGTH = void 0;
|
|
4
|
+
const contact_messenger_enum_1 = require("../enum/contact-messenger.enum");
|
|
5
|
+
// Ограничение на длину списка контактов вебшопа: блок контактов рисуется в шапке и в мобильном
|
|
6
|
+
// меню, больше десятка номеров туда не помещается физически.
|
|
7
|
+
exports.CONTACT_PHONE_LIST_MAX_LENGTH = 10;
|
|
8
|
+
// Потолки для полей одного контакта. Список лежит в JSON-колонке, где длину не держит тип колонки,
|
|
9
|
+
// поэтому единственная граница — схема. Номер с кодом страны и разделителями в 32 символа
|
|
10
|
+
// укладывается с запасом, подпись отдела («отдел продаж», «склад») — в 64.
|
|
11
|
+
exports.CONTACT_PHONE_MAX_LENGTH = 32;
|
|
12
|
+
exports.CONTACT_PHONE_LABEL_MAX_LENGTH = 64;
|
|
13
|
+
// Ссылка на мессенджер. Домены не фиксируем — max.ru уже отдаёт три разных формата (`/u/<хеш>`,
|
|
14
|
+
// `/@bot`, `/join/<код>`), и привязка к хосту означала бы деплой на каждое изменение у мессенджера.
|
|
15
|
+
exports.CONTACT_MESSENGER_URL_MAX_LENGTH = 255;
|
|
16
|
+
// Мессенджеры, до которых нельзя дойти по номеру телефона: у них ссылка обязательна.
|
|
17
|
+
// WhatsApp единственный выводится из номера — `https://wa.me/<цифры>`.
|
|
18
|
+
// Telegram по номеру открывается только схемой `tg://` в установленном приложении, в вебе не
|
|
19
|
+
// работает, поэтому нужен юзернейм. У MAX маршрута по номеру нет вовсе — только профиль по хешу
|
|
20
|
+
// из QR-кода, `@бот` или инвайт в канал.
|
|
21
|
+
exports.MESSENGERS_REQUIRING_URL = [contact_messenger_enum_1.ContactMessengerEnum.TELEGRAM, contact_messenger_enum_1.ContactMessengerEnum.MAX];
|
package/build/constant/error.js
CHANGED
|
@@ -81,6 +81,9 @@ exports.ERRORS = {
|
|
|
81
81
|
SETTINGS_NOT_UPDATED: { code: 'S003', message: 'Failed to update settings', httpCode: 500 },
|
|
82
82
|
SETTINGS_NOT_FOUND: { code: 'S005', message: 'Settings not found', httpCode: 404 },
|
|
83
83
|
SETTINGS_FETCH_FAILED: { code: 'S006', message: 'Error when retrieving a Settings', httpCode: 500 },
|
|
84
|
+
// 409, а не 403: фильтр исключений маскирует текст любого 403 общим «Access denied», а здесь
|
|
85
|
+
// клиенту важно получить причину — заказы отключены временно, доступ ни при чём.
|
|
86
|
+
ORDERS_DISABLED_BY_SETTINGS: { code: 'S007', message: 'Ordering is temporarily disabled by the company', httpCode: 409 },
|
|
84
87
|
FORBIDDEN_API: { code: 'A011', message: 'Access denied', httpCode: 403 },
|
|
85
88
|
RESOURCE_ACCESS_DENIED: { code: 'A022', message: 'Resource does not belong to your company', httpCode: 400 },
|
|
86
89
|
RESOURCE_NOT_FOUND: { code: 'A023', message: 'Resource not found', httpCode: 404 },
|
package/build/constant/index.js
CHANGED
|
@@ -21,3 +21,5 @@ __exportStar(require("./import.constant"), exports);
|
|
|
21
21
|
__exportStar(require("./validation-i18n-key"), exports);
|
|
22
22
|
__exportStar(require("./validation-i18n-message"), exports);
|
|
23
23
|
__exportStar(require("./report.constant"), exports);
|
|
24
|
+
__exportStar(require("./contact-phone.constant"), exports);
|
|
25
|
+
__exportStar(require("./settings.constant"), exports);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DISABLE_ORDERS_MESSAGE_MAX_LENGTH = void 0;
|
|
4
|
+
// Текст режима «запрет заказов» («Возобновим работу в 16.00 после инвентаризации»). Витрина рисует
|
|
5
|
+
// его баннером вместо каталога, поэтому это короткое объявление, а не страница: 300 символов.
|
|
6
|
+
exports.DISABLE_ORDERS_MESSAGE_MAX_LENGTH = 300;
|
|
@@ -36,4 +36,6 @@ exports.ValidationI18nKey = {
|
|
|
36
36
|
standingOrderCategoryRequired: 'validation.standingOrderCategoryRequired',
|
|
37
37
|
standingOrderNameRequired: 'validation.standingOrderNameRequired',
|
|
38
38
|
standingOrderQuantityRequired: 'validation.standingOrderQuantityRequired',
|
|
39
|
+
contactMessengerUrlRequired: 'validation.contactMessengerUrlRequired',
|
|
40
|
+
contactMessengerDuplicate: 'validation.contactMessengerDuplicate',
|
|
39
41
|
};
|
|
@@ -39,6 +39,8 @@ exports.VALIDATION_I18N_MESSAGE = {
|
|
|
39
39
|
[validation_i18n_key_1.ValidationI18nKey.standingOrderCategoryRequired]: 'Category is required',
|
|
40
40
|
[validation_i18n_key_1.ValidationI18nKey.standingOrderNameRequired]: 'Name is required',
|
|
41
41
|
[validation_i18n_key_1.ValidationI18nKey.standingOrderQuantityRequired]: 'Quantity is required',
|
|
42
|
+
[validation_i18n_key_1.ValidationI18nKey.contactMessengerUrlRequired]: 'This messenger cannot be reached by phone number, provide a link',
|
|
43
|
+
[validation_i18n_key_1.ValidationI18nKey.contactMessengerDuplicate]: 'The same messenger is listed twice for this contact',
|
|
42
44
|
};
|
|
43
45
|
/**
|
|
44
46
|
* A validation `message` is a stable i18n token when it matches this pattern (e.g. `validation.dateRange`).
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ContactMessengerEnum = void 0;
|
|
4
|
+
var ContactMessengerEnum;
|
|
5
|
+
(function (ContactMessengerEnum) {
|
|
6
|
+
ContactMessengerEnum["WHATSAPP"] = "whatsapp";
|
|
7
|
+
ContactMessengerEnum["TELEGRAM"] = "telegram";
|
|
8
|
+
ContactMessengerEnum["MAX"] = "max";
|
|
9
|
+
})(ContactMessengerEnum || (exports.ContactMessengerEnum = ContactMessengerEnum = {}));
|
package/build/enum/index.js
CHANGED
|
@@ -97,6 +97,7 @@ __exportStar(require("./order-webshop-available-status-list"), exports);
|
|
|
97
97
|
__exportStar(require("./transaction-type-groups"), exports);
|
|
98
98
|
__exportStar(require("./chat-has-manager-mode.enum"), exports);
|
|
99
99
|
__exportStar(require("./chat-no-manager-mode.enum"), exports);
|
|
100
|
+
__exportStar(require("./contact-messenger.enum"), exports);
|
|
100
101
|
__exportStar(require("./match-confidence.enum"), exports);
|
|
101
102
|
__exportStar(require("./session-platform.enum"), exports);
|
|
102
103
|
__exportStar(require("./device-platform.enum"), exports);
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.UpdateSettingsRequestSchema = exports.CreateSettingsSchema = exports.SettingsForAdminSchema = exports.SettingsSchema = exports.DeliveryPricingTierSchema = exports.ImportantMessageSchema = exports.SettingsWorkingHoursSchema = void 0;
|
|
3
|
+
exports.UpdateSettingsRequestSchema = exports.CreateSettingsSchema = exports.SettingsForAdminSchema = exports.SettingsSchema = exports.DeliveryPricingTierSchema = exports.DisableOrdersMessageSchema = exports.ContactPhoneListSchema = exports.ContactPhoneSchema = exports.ContactMessengerLinkSchema = exports.ImportantMessageSchema = exports.SettingsWorkingHoursSchema = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const enum_1 = require("../../enum");
|
|
6
|
+
const contact_phone_constant_1 = require("../../constant/contact-phone.constant");
|
|
7
|
+
const settings_constant_1 = require("../../constant/settings.constant");
|
|
8
|
+
const validation_i18n_key_1 = require("../../constant/validation-i18n-key");
|
|
6
9
|
const webshop_main_display_mode_enum_1 = require("../../enum/webshop-main-display-mode.enum");
|
|
7
10
|
const menu_items_url_enum_1 = require("../../enum/menu-items-url.enum");
|
|
8
11
|
const non_negative_money_schema_1 = require("../non-negative-money.schema");
|
|
@@ -21,6 +24,44 @@ exports.ImportantMessageSchema = zod_1.z.object({
|
|
|
21
24
|
description: zod_1.z.string(),
|
|
22
25
|
isActive: zod_1.z.boolean(),
|
|
23
26
|
});
|
|
27
|
+
// Мессенджер контакта. `url === null` означает «собрать ссылку из номера» — так умеет только
|
|
28
|
+
// WhatsApp, поэтому для остальных ссылка обязательна (см. MESSENGERS_REQUIRING_URL и проверку ниже).
|
|
29
|
+
exports.ContactMessengerLinkSchema = zod_1.z.object({
|
|
30
|
+
type: zod_1.z.nativeEnum(enum_1.ContactMessengerEnum),
|
|
31
|
+
url: zod_1.z.string().url().max(contact_phone_constant_1.CONTACT_MESSENGER_URL_MAX_LENGTH).nullable(),
|
|
32
|
+
});
|
|
33
|
+
// Один контакт из блока контактов вебшопа: номер, подпись отдела («отдел продаж», «склад») и
|
|
34
|
+
// мессенджеры, доступные по этому же номеру. Порядок вывода задаётся порядком элементов в массиве.
|
|
35
|
+
exports.ContactPhoneSchema = zod_1.z
|
|
36
|
+
.object({
|
|
37
|
+
phone: zod_1.z.string().min(1).max(contact_phone_constant_1.CONTACT_PHONE_MAX_LENGTH),
|
|
38
|
+
label: zod_1.z.string().max(contact_phone_constant_1.CONTACT_PHONE_LABEL_MAX_LENGTH).nullable(),
|
|
39
|
+
messengers: zod_1.z.array(exports.ContactMessengerLinkSchema),
|
|
40
|
+
})
|
|
41
|
+
.superRefine(({ messengers }, ctx) => {
|
|
42
|
+
const seen = new Set();
|
|
43
|
+
messengers.forEach((messenger, index) => {
|
|
44
|
+
if (!messenger.url && contact_phone_constant_1.MESSENGERS_REQUIRING_URL.includes(messenger.type)) {
|
|
45
|
+
ctx.addIssue({
|
|
46
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
47
|
+
path: ['messengers', index, 'url'],
|
|
48
|
+
message: validation_i18n_key_1.ValidationI18nKey.contactMessengerUrlRequired,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
if (seen.has(messenger.type)) {
|
|
52
|
+
ctx.addIssue({
|
|
53
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
54
|
+
path: ['messengers', index, 'type'],
|
|
55
|
+
message: validation_i18n_key_1.ValidationI18nKey.contactMessengerDuplicate,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
seen.add(messenger.type);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
exports.ContactPhoneListSchema = zod_1.z.array(exports.ContactPhoneSchema).max(contact_phone_constant_1.CONTACT_PHONE_LIST_MAX_LENGTH);
|
|
62
|
+
// Один потолок на все три схемы настроек: колонка `disable_orders_message` — TEXT, длину держит
|
|
63
|
+
// только валидация.
|
|
64
|
+
exports.DisableOrdersMessageSchema = zod_1.z.string().max(settings_constant_1.DISABLE_ORDERS_MESSAGE_MAX_LENGTH);
|
|
24
65
|
// Обе величины — деньги: `minSubtotal` сравнивается с суммой заказа, `cost` попадает в
|
|
25
66
|
// `order.delivery_cost`. Живут они внутри JSON-колонки, куда ALTER TABLE не достаёт, поэтому
|
|
26
67
|
// точность здесь держит не тип колонки, а формат значения — та же десятичная строка, что и на проводе.
|
|
@@ -72,6 +113,8 @@ exports.SettingsSchema = zod_1.z.object({
|
|
|
72
113
|
phoneSales: zod_1.z.string().nullable(),
|
|
73
114
|
phoneWhatsappSales: zod_1.z.string().nullable(),
|
|
74
115
|
telegramChannel: zod_1.z.string().nullable(),
|
|
116
|
+
maxChannel: zod_1.z.string().nullable(),
|
|
117
|
+
contactPhoneList: exports.ContactPhoneListSchema.nullable(),
|
|
75
118
|
menuItemsSort: zod_1.z.array(zod_1.z.nativeEnum(menu_items_url_enum_1.MenuItemsUrlEnum)).nullable(),
|
|
76
119
|
phoneCountryCode: zod_1.z.array(zod_1.z.string()),
|
|
77
120
|
aboutCompany: zod_1.z.string().nullable(),
|
|
@@ -82,6 +125,7 @@ exports.SettingsSchema = zod_1.z.object({
|
|
|
82
125
|
deliveryPolicy: zod_1.z.string().nullable(),
|
|
83
126
|
writeOffPolicy: zod_1.z.string().nullable(),
|
|
84
127
|
disableOrders: zod_1.z.boolean(),
|
|
128
|
+
disableOrdersMessage: exports.DisableOrdersMessageSchema.nullable(),
|
|
85
129
|
bankHookEnabled: zod_1.z.coerce.boolean().optional(),
|
|
86
130
|
loginImageList: zod_1.z.array(zod_1.z.string()),
|
|
87
131
|
loginImageKeyList: zod_1.z.array(zod_1.z.string()),
|
|
@@ -158,6 +202,8 @@ exports.CreateSettingsSchema = zod_1.z.object({
|
|
|
158
202
|
phoneSales: zod_1.z.string().nullable().optional(),
|
|
159
203
|
phoneWhatsappSales: zod_1.z.string().nullable().optional(),
|
|
160
204
|
telegramChannel: zod_1.z.string().nullable().optional(),
|
|
205
|
+
maxChannel: zod_1.z.string().nullable().optional(),
|
|
206
|
+
contactPhoneList: exports.ContactPhoneListSchema.nullable().optional(),
|
|
161
207
|
menuItemsSort: zod_1.z.array(zod_1.z.nativeEnum(menu_items_url_enum_1.MenuItemsUrlEnum)).nullable().optional(),
|
|
162
208
|
phoneCountryCode: zod_1.z.array(zod_1.z.string()).optional(),
|
|
163
209
|
aboutCompany: zod_1.z.string().nullable().optional(),
|
|
@@ -168,6 +214,7 @@ exports.CreateSettingsSchema = zod_1.z.object({
|
|
|
168
214
|
deliveryPolicy: zod_1.z.string().nullable().optional(),
|
|
169
215
|
writeOffPolicy: zod_1.z.string().nullable().optional(),
|
|
170
216
|
disableOrders: zod_1.z.boolean().optional(),
|
|
217
|
+
disableOrdersMessage: exports.DisableOrdersMessageSchema.nullable().optional(),
|
|
171
218
|
isMessengerEnabled: zod_1.z.coerce.boolean().optional(),
|
|
172
219
|
isWriteOffAcceptanceEnabled: zod_1.z.coerce.boolean().optional(),
|
|
173
220
|
isInvoiceDownloadEnabled: zod_1.z.coerce.boolean().optional(),
|
|
@@ -219,6 +266,8 @@ exports.UpdateSettingsRequestSchema = zod_1.z.object({
|
|
|
219
266
|
phoneSales: zod_1.z.string().nullable().optional(),
|
|
220
267
|
phoneWhatsappSales: zod_1.z.string().nullable().optional(),
|
|
221
268
|
telegramChannel: zod_1.z.string().nullable().optional(),
|
|
269
|
+
maxChannel: zod_1.z.string().nullable().optional(),
|
|
270
|
+
contactPhoneList: exports.ContactPhoneListSchema.nullable().optional(),
|
|
222
271
|
menuItemsSort: zod_1.z.array(zod_1.z.nativeEnum(menu_items_url_enum_1.MenuItemsUrlEnum)).nullable().optional(),
|
|
223
272
|
phoneCountryCode: zod_1.z.array(zod_1.z.string()).optional(),
|
|
224
273
|
aboutCompany: zod_1.z.string().nullable().optional(),
|
|
@@ -229,6 +278,7 @@ exports.UpdateSettingsRequestSchema = zod_1.z.object({
|
|
|
229
278
|
deliveryPolicy: zod_1.z.string().nullable().optional(),
|
|
230
279
|
writeOffPolicy: zod_1.z.string().nullable().optional(),
|
|
231
280
|
disableOrders: zod_1.z.boolean().optional(),
|
|
281
|
+
disableOrdersMessage: exports.DisableOrdersMessageSchema.nullable().optional(),
|
|
232
282
|
isMessengerEnabled: zod_1.z.coerce.boolean().optional(),
|
|
233
283
|
isWriteOffAcceptanceEnabled: zod_1.z.coerce.boolean().optional(),
|
|
234
284
|
isInvoiceDownloadEnabled: zod_1.z.coerce.boolean().optional(),
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ContactMessengerEnum } from '../enum/contact-messenger.enum';
|
|
2
|
+
|
|
3
|
+
// Ограничение на длину списка контактов вебшопа: блок контактов рисуется в шапке и в мобильном
|
|
4
|
+
// меню, больше десятка номеров туда не помещается физически.
|
|
5
|
+
export const CONTACT_PHONE_LIST_MAX_LENGTH = 10;
|
|
6
|
+
|
|
7
|
+
// Потолки для полей одного контакта. Список лежит в JSON-колонке, где длину не держит тип колонки,
|
|
8
|
+
// поэтому единственная граница — схема. Номер с кодом страны и разделителями в 32 символа
|
|
9
|
+
// укладывается с запасом, подпись отдела («отдел продаж», «склад») — в 64.
|
|
10
|
+
export const CONTACT_PHONE_MAX_LENGTH = 32;
|
|
11
|
+
export const CONTACT_PHONE_LABEL_MAX_LENGTH = 64;
|
|
12
|
+
|
|
13
|
+
// Ссылка на мессенджер. Домены не фиксируем — max.ru уже отдаёт три разных формата (`/u/<хеш>`,
|
|
14
|
+
// `/@bot`, `/join/<код>`), и привязка к хосту означала бы деплой на каждое изменение у мессенджера.
|
|
15
|
+
export const CONTACT_MESSENGER_URL_MAX_LENGTH = 255;
|
|
16
|
+
|
|
17
|
+
// Мессенджеры, до которых нельзя дойти по номеру телефона: у них ссылка обязательна.
|
|
18
|
+
// WhatsApp единственный выводится из номера — `https://wa.me/<цифры>`.
|
|
19
|
+
// Telegram по номеру открывается только схемой `tg://` в установленном приложении, в вебе не
|
|
20
|
+
// работает, поэтому нужен юзернейм. У MAX маршрута по номеру нет вовсе — только профиль по хешу
|
|
21
|
+
// из QR-кода, `@бот` или инвайт в канал.
|
|
22
|
+
export const MESSENGERS_REQUIRING_URL: ContactMessengerEnum[] = [ContactMessengerEnum.TELEGRAM, ContactMessengerEnum.MAX];
|
package/constant/error.ts
CHANGED
|
@@ -81,6 +81,9 @@ export const ERRORS = {
|
|
|
81
81
|
SETTINGS_NOT_UPDATED: { code: 'S003', message: 'Failed to update settings', httpCode: 500 },
|
|
82
82
|
SETTINGS_NOT_FOUND: { code: 'S005', message: 'Settings not found', httpCode: 404 },
|
|
83
83
|
SETTINGS_FETCH_FAILED: { code: 'S006', message: 'Error when retrieving a Settings', httpCode: 500 },
|
|
84
|
+
// 409, а не 403: фильтр исключений маскирует текст любого 403 общим «Access denied», а здесь
|
|
85
|
+
// клиенту важно получить причину — заказы отключены временно, доступ ни при чём.
|
|
86
|
+
ORDERS_DISABLED_BY_SETTINGS: { code: 'S007', message: 'Ordering is temporarily disabled by the company', httpCode: 409 },
|
|
84
87
|
|
|
85
88
|
FORBIDDEN_API: { code: 'A011', message: 'Access denied', httpCode: 403 },
|
|
86
89
|
RESOURCE_ACCESS_DENIED: { code: 'A022', message: 'Resource does not belong to your company', httpCode: 400 },
|
package/constant/index.ts
CHANGED
|
@@ -33,6 +33,8 @@ export const ValidationI18nKey = {
|
|
|
33
33
|
standingOrderCategoryRequired: 'validation.standingOrderCategoryRequired',
|
|
34
34
|
standingOrderNameRequired: 'validation.standingOrderNameRequired',
|
|
35
35
|
standingOrderQuantityRequired: 'validation.standingOrderQuantityRequired',
|
|
36
|
+
contactMessengerUrlRequired: 'validation.contactMessengerUrlRequired',
|
|
37
|
+
contactMessengerDuplicate: 'validation.contactMessengerDuplicate',
|
|
36
38
|
} as const;
|
|
37
39
|
|
|
38
40
|
export type ValidationI18nKeyType = (typeof ValidationI18nKey)[keyof typeof ValidationI18nKey];
|
|
@@ -37,6 +37,8 @@ export const VALIDATION_I18N_MESSAGE: Record<ValidationI18nKeyType, string> = {
|
|
|
37
37
|
[ValidationI18nKey.standingOrderCategoryRequired]: 'Category is required',
|
|
38
38
|
[ValidationI18nKey.standingOrderNameRequired]: 'Name is required',
|
|
39
39
|
[ValidationI18nKey.standingOrderQuantityRequired]: 'Quantity is required',
|
|
40
|
+
[ValidationI18nKey.contactMessengerUrlRequired]: 'This messenger cannot be reached by phone number, provide a link',
|
|
41
|
+
[ValidationI18nKey.contactMessengerDuplicate]: 'The same messenger is listed twice for this contact',
|
|
40
42
|
};
|
|
41
43
|
|
|
42
44
|
/**
|
package/enum/index.ts
CHANGED
|
@@ -81,6 +81,7 @@ export * from './order-webshop-available-status-list';
|
|
|
81
81
|
export * from './transaction-type-groups';
|
|
82
82
|
export * from './chat-has-manager-mode.enum';
|
|
83
83
|
export * from './chat-no-manager-mode.enum';
|
|
84
|
+
export * from './contact-messenger.enum';
|
|
84
85
|
export * from './match-confidence.enum';
|
|
85
86
|
export * from './session-platform.enum';
|
|
86
87
|
export * from './device-platform.enum';
|
package/package.json
CHANGED
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
ChatHasManagerModeEnum,
|
|
4
|
+
ChatNoManagerModeEnum,
|
|
5
|
+
ContactMessengerEnum,
|
|
6
|
+
DiscountPriceRoundingEnum,
|
|
7
|
+
LanguageEnum,
|
|
8
|
+
WebshopModeEnum,
|
|
9
|
+
} from '../../enum';
|
|
10
|
+
import {
|
|
11
|
+
CONTACT_MESSENGER_URL_MAX_LENGTH,
|
|
12
|
+
CONTACT_PHONE_LABEL_MAX_LENGTH,
|
|
13
|
+
CONTACT_PHONE_LIST_MAX_LENGTH,
|
|
14
|
+
CONTACT_PHONE_MAX_LENGTH,
|
|
15
|
+
MESSENGERS_REQUIRING_URL,
|
|
16
|
+
} from '../../constant/contact-phone.constant';
|
|
17
|
+
import { DISABLE_ORDERS_MESSAGE_MAX_LENGTH } from '../../constant/settings.constant';
|
|
18
|
+
import { ValidationI18nKey } from '../../constant/validation-i18n-key';
|
|
3
19
|
import { WebshopMainDisplayModeEnum } from '../../enum/webshop-main-display-mode.enum';
|
|
4
20
|
import { MenuItemsUrlEnum } from '../../enum/menu-items-url.enum';
|
|
5
|
-
import { MoneySchema } from '../money.schema';
|
|
6
21
|
import { NonNegativeMoneySchema } from '../non-negative-money.schema';
|
|
7
22
|
|
|
8
23
|
export const SettingsWorkingHoursSchema = z.object({
|
|
@@ -22,6 +37,50 @@ export const ImportantMessageSchema = z.object({
|
|
|
22
37
|
isActive: z.boolean(),
|
|
23
38
|
});
|
|
24
39
|
|
|
40
|
+
// Мессенджер контакта. `url === null` означает «собрать ссылку из номера» — так умеет только
|
|
41
|
+
// WhatsApp, поэтому для остальных ссылка обязательна (см. MESSENGERS_REQUIRING_URL и проверку ниже).
|
|
42
|
+
export const ContactMessengerLinkSchema = z.object({
|
|
43
|
+
type: z.nativeEnum(ContactMessengerEnum),
|
|
44
|
+
url: z.string().url().max(CONTACT_MESSENGER_URL_MAX_LENGTH).nullable(),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Один контакт из блока контактов вебшопа: номер, подпись отдела («отдел продаж», «склад») и
|
|
48
|
+
// мессенджеры, доступные по этому же номеру. Порядок вывода задаётся порядком элементов в массиве.
|
|
49
|
+
export const ContactPhoneSchema = z
|
|
50
|
+
.object({
|
|
51
|
+
phone: z.string().min(1).max(CONTACT_PHONE_MAX_LENGTH),
|
|
52
|
+
label: z.string().max(CONTACT_PHONE_LABEL_MAX_LENGTH).nullable(),
|
|
53
|
+
messengers: z.array(ContactMessengerLinkSchema),
|
|
54
|
+
})
|
|
55
|
+
.superRefine(({ messengers }, ctx) => {
|
|
56
|
+
const seen = new Set<ContactMessengerEnum>();
|
|
57
|
+
|
|
58
|
+
messengers.forEach((messenger, index) => {
|
|
59
|
+
if (!messenger.url && MESSENGERS_REQUIRING_URL.includes(messenger.type)) {
|
|
60
|
+
ctx.addIssue({
|
|
61
|
+
code: z.ZodIssueCode.custom,
|
|
62
|
+
path: ['messengers', index, 'url'],
|
|
63
|
+
message: ValidationI18nKey.contactMessengerUrlRequired,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (seen.has(messenger.type)) {
|
|
68
|
+
ctx.addIssue({
|
|
69
|
+
code: z.ZodIssueCode.custom,
|
|
70
|
+
path: ['messengers', index, 'type'],
|
|
71
|
+
message: ValidationI18nKey.contactMessengerDuplicate,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
seen.add(messenger.type);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
export const ContactPhoneListSchema = z.array(ContactPhoneSchema).max(CONTACT_PHONE_LIST_MAX_LENGTH);
|
|
79
|
+
|
|
80
|
+
// Один потолок на все три схемы настроек: колонка `disable_orders_message` — TEXT, длину держит
|
|
81
|
+
// только валидация.
|
|
82
|
+
export const DisableOrdersMessageSchema = z.string().max(DISABLE_ORDERS_MESSAGE_MAX_LENGTH);
|
|
83
|
+
|
|
25
84
|
// Обе величины — деньги: `minSubtotal` сравнивается с суммой заказа, `cost` попадает в
|
|
26
85
|
// `order.delivery_cost`. Живут они внутри JSON-колонки, куда ALTER TABLE не достаёт, поэтому
|
|
27
86
|
// точность здесь держит не тип колонки, а формат значения — та же десятичная строка, что и на проводе.
|
|
@@ -74,6 +133,8 @@ export const SettingsSchema = z.object({
|
|
|
74
133
|
phoneSales: z.string().nullable(),
|
|
75
134
|
phoneWhatsappSales: z.string().nullable(),
|
|
76
135
|
telegramChannel: z.string().nullable(),
|
|
136
|
+
maxChannel: z.string().nullable(),
|
|
137
|
+
contactPhoneList: ContactPhoneListSchema.nullable(),
|
|
77
138
|
menuItemsSort: z.array(z.nativeEnum(MenuItemsUrlEnum)).nullable(),
|
|
78
139
|
phoneCountryCode: z.array(z.string()),
|
|
79
140
|
aboutCompany: z.string().nullable(),
|
|
@@ -84,6 +145,7 @@ export const SettingsSchema = z.object({
|
|
|
84
145
|
deliveryPolicy: z.string().nullable(),
|
|
85
146
|
writeOffPolicy: z.string().nullable(),
|
|
86
147
|
disableOrders: z.boolean(),
|
|
148
|
+
disableOrdersMessage: DisableOrdersMessageSchema.nullable(),
|
|
87
149
|
bankHookEnabled: z.coerce.boolean().optional(),
|
|
88
150
|
loginImageList: z.array(z.string()),
|
|
89
151
|
loginImageKeyList: z.array(z.string()),
|
|
@@ -164,6 +226,8 @@ export const CreateSettingsSchema = z.object({
|
|
|
164
226
|
phoneSales: z.string().nullable().optional(),
|
|
165
227
|
phoneWhatsappSales: z.string().nullable().optional(),
|
|
166
228
|
telegramChannel: z.string().nullable().optional(),
|
|
229
|
+
maxChannel: z.string().nullable().optional(),
|
|
230
|
+
contactPhoneList: ContactPhoneListSchema.nullable().optional(),
|
|
167
231
|
menuItemsSort: z.array(z.nativeEnum(MenuItemsUrlEnum)).nullable().optional(),
|
|
168
232
|
phoneCountryCode: z.array(z.string()).optional(),
|
|
169
233
|
aboutCompany: z.string().nullable().optional(),
|
|
@@ -174,6 +238,7 @@ export const CreateSettingsSchema = z.object({
|
|
|
174
238
|
deliveryPolicy: z.string().nullable().optional(),
|
|
175
239
|
writeOffPolicy: z.string().nullable().optional(),
|
|
176
240
|
disableOrders: z.boolean().optional(),
|
|
241
|
+
disableOrdersMessage: DisableOrdersMessageSchema.nullable().optional(),
|
|
177
242
|
isMessengerEnabled: z.coerce.boolean().optional(),
|
|
178
243
|
isWriteOffAcceptanceEnabled: z.coerce.boolean().optional(),
|
|
179
244
|
isInvoiceDownloadEnabled: z.coerce.boolean().optional(),
|
|
@@ -228,6 +293,8 @@ export const UpdateSettingsRequestSchema = z.object({
|
|
|
228
293
|
phoneSales: z.string().nullable().optional(),
|
|
229
294
|
phoneWhatsappSales: z.string().nullable().optional(),
|
|
230
295
|
telegramChannel: z.string().nullable().optional(),
|
|
296
|
+
maxChannel: z.string().nullable().optional(),
|
|
297
|
+
contactPhoneList: ContactPhoneListSchema.nullable().optional(),
|
|
231
298
|
menuItemsSort: z.array(z.nativeEnum(MenuItemsUrlEnum)).nullable().optional(),
|
|
232
299
|
phoneCountryCode: z.array(z.string()).optional(),
|
|
233
300
|
aboutCompany: z.string().nullable().optional(),
|
|
@@ -238,6 +305,7 @@ export const UpdateSettingsRequestSchema = z.object({
|
|
|
238
305
|
deliveryPolicy: z.string().nullable().optional(),
|
|
239
306
|
writeOffPolicy: z.string().nullable().optional(),
|
|
240
307
|
disableOrders: z.boolean().optional(),
|
|
308
|
+
disableOrdersMessage: DisableOrdersMessageSchema.nullable().optional(),
|
|
241
309
|
isMessengerEnabled: z.coerce.boolean().optional(),
|
|
242
310
|
isWriteOffAcceptanceEnabled: z.coerce.boolean().optional(),
|
|
243
311
|
isInvoiceDownloadEnabled: z.coerce.boolean().optional(),
|