@floristcloud/api-lib 1.2.113 → 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.
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CONTACT_PHONE_LABEL_MAX_LENGTH = exports.CONTACT_PHONE_MAX_LENGTH = exports.CONTACT_PHONE_LIST_MAX_LENGTH = void 0;
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");
4
5
  // Ограничение на длину списка контактов вебшопа: блок контактов рисуется в шапке и в мобильном
5
6
  // меню, больше десятка номеров туда не помещается физически.
6
7
  exports.CONTACT_PHONE_LIST_MAX_LENGTH = 10;
@@ -9,3 +10,12 @@ exports.CONTACT_PHONE_LIST_MAX_LENGTH = 10;
9
10
  // укладывается с запасом, подпись отдела («отдел продаж», «склад») — в 64.
10
11
  exports.CONTACT_PHONE_MAX_LENGTH = 32;
11
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];
@@ -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`).
@@ -1,10 +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.DisableOrdersMessageSchema = exports.ContactPhoneListSchema = exports.ContactPhoneSchema = 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
6
  const contact_phone_constant_1 = require("../../constant/contact-phone.constant");
7
7
  const settings_constant_1 = require("../../constant/settings.constant");
8
+ const validation_i18n_key_1 = require("../../constant/validation-i18n-key");
8
9
  const webshop_main_display_mode_enum_1 = require("../../enum/webshop-main-display-mode.enum");
9
10
  const menu_items_url_enum_1 = require("../../enum/menu-items-url.enum");
10
11
  const non_negative_money_schema_1 = require("../non-negative-money.schema");
@@ -23,12 +24,39 @@ exports.ImportantMessageSchema = zod_1.z.object({
23
24
  description: zod_1.z.string(),
24
25
  isActive: zod_1.z.boolean(),
25
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
+ });
26
33
  // Один контакт из блока контактов вебшопа: номер, подпись отдела («отдел продаж», «склад») и
27
34
  // мессенджеры, доступные по этому же номеру. Порядок вывода задаётся порядком элементов в массиве.
28
- exports.ContactPhoneSchema = zod_1.z.object({
35
+ exports.ContactPhoneSchema = zod_1.z
36
+ .object({
29
37
  phone: zod_1.z.string().min(1).max(contact_phone_constant_1.CONTACT_PHONE_MAX_LENGTH),
30
38
  label: zod_1.z.string().max(contact_phone_constant_1.CONTACT_PHONE_LABEL_MAX_LENGTH).nullable(),
31
- messengers: zod_1.z.array(zod_1.z.nativeEnum(enum_1.ContactMessengerEnum)),
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
+ });
32
60
  });
33
61
  exports.ContactPhoneListSchema = zod_1.z.array(exports.ContactPhoneSchema).max(contact_phone_constant_1.CONTACT_PHONE_LIST_MAX_LENGTH);
34
62
  // Один потолок на все три схемы настроек: колонка `disable_orders_message` — TEXT, длину держит
@@ -85,6 +113,7 @@ exports.SettingsSchema = zod_1.z.object({
85
113
  phoneSales: zod_1.z.string().nullable(),
86
114
  phoneWhatsappSales: zod_1.z.string().nullable(),
87
115
  telegramChannel: zod_1.z.string().nullable(),
116
+ maxChannel: zod_1.z.string().nullable(),
88
117
  contactPhoneList: exports.ContactPhoneListSchema.nullable(),
89
118
  menuItemsSort: zod_1.z.array(zod_1.z.nativeEnum(menu_items_url_enum_1.MenuItemsUrlEnum)).nullable(),
90
119
  phoneCountryCode: zod_1.z.array(zod_1.z.string()),
@@ -173,6 +202,7 @@ exports.CreateSettingsSchema = zod_1.z.object({
173
202
  phoneSales: zod_1.z.string().nullable().optional(),
174
203
  phoneWhatsappSales: zod_1.z.string().nullable().optional(),
175
204
  telegramChannel: zod_1.z.string().nullable().optional(),
205
+ maxChannel: zod_1.z.string().nullable().optional(),
176
206
  contactPhoneList: exports.ContactPhoneListSchema.nullable().optional(),
177
207
  menuItemsSort: zod_1.z.array(zod_1.z.nativeEnum(menu_items_url_enum_1.MenuItemsUrlEnum)).nullable().optional(),
178
208
  phoneCountryCode: zod_1.z.array(zod_1.z.string()).optional(),
@@ -236,6 +266,7 @@ exports.UpdateSettingsRequestSchema = zod_1.z.object({
236
266
  phoneSales: zod_1.z.string().nullable().optional(),
237
267
  phoneWhatsappSales: zod_1.z.string().nullable().optional(),
238
268
  telegramChannel: zod_1.z.string().nullable().optional(),
269
+ maxChannel: zod_1.z.string().nullable().optional(),
239
270
  contactPhoneList: exports.ContactPhoneListSchema.nullable().optional(),
240
271
  menuItemsSort: zod_1.z.array(zod_1.z.nativeEnum(menu_items_url_enum_1.MenuItemsUrlEnum)).nullable().optional(),
241
272
  phoneCountryCode: zod_1.z.array(zod_1.z.string()).optional(),
@@ -1,3 +1,5 @@
1
+ import { ContactMessengerEnum } from '../enum/contact-messenger.enum';
2
+
1
3
  // Ограничение на длину списка контактов вебшопа: блок контактов рисуется в шапке и в мобильном
2
4
  // меню, больше десятка номеров туда не помещается физически.
3
5
  export const CONTACT_PHONE_LIST_MAX_LENGTH = 10;
@@ -7,3 +9,14 @@ export const CONTACT_PHONE_LIST_MAX_LENGTH = 10;
7
9
  // укладывается с запасом, подпись отдела («отдел продаж», «склад») — в 64.
8
10
  export const CONTACT_PHONE_MAX_LENGTH = 32;
9
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];
@@ -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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@floristcloud/api-lib",
3
- "version": "1.2.113",
3
+ "version": "1.2.114",
4
4
  "description": "",
5
5
  "main": "./build/index.js",
6
6
  "scripts": {
@@ -8,14 +8,16 @@ import {
8
8
  WebshopModeEnum,
9
9
  } from '../../enum';
10
10
  import {
11
+ CONTACT_MESSENGER_URL_MAX_LENGTH,
11
12
  CONTACT_PHONE_LABEL_MAX_LENGTH,
12
13
  CONTACT_PHONE_LIST_MAX_LENGTH,
13
14
  CONTACT_PHONE_MAX_LENGTH,
15
+ MESSENGERS_REQUIRING_URL,
14
16
  } from '../../constant/contact-phone.constant';
15
17
  import { DISABLE_ORDERS_MESSAGE_MAX_LENGTH } from '../../constant/settings.constant';
18
+ import { ValidationI18nKey } from '../../constant/validation-i18n-key';
16
19
  import { WebshopMainDisplayModeEnum } from '../../enum/webshop-main-display-mode.enum';
17
20
  import { MenuItemsUrlEnum } from '../../enum/menu-items-url.enum';
18
- import { MoneySchema } from '../money.schema';
19
21
  import { NonNegativeMoneySchema } from '../non-negative-money.schema';
20
22
 
21
23
  export const SettingsWorkingHoursSchema = z.object({
@@ -35,13 +37,43 @@ export const ImportantMessageSchema = z.object({
35
37
  isActive: z.boolean(),
36
38
  });
37
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
+
38
47
  // Один контакт из блока контактов вебшопа: номер, подпись отдела («отдел продаж», «склад») и
39
48
  // мессенджеры, доступные по этому же номеру. Порядок вывода задаётся порядком элементов в массиве.
40
- export const ContactPhoneSchema = z.object({
41
- phone: z.string().min(1).max(CONTACT_PHONE_MAX_LENGTH),
42
- label: z.string().max(CONTACT_PHONE_LABEL_MAX_LENGTH).nullable(),
43
- messengers: z.array(z.nativeEnum(ContactMessengerEnum)),
44
- });
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
+ });
45
77
 
46
78
  export const ContactPhoneListSchema = z.array(ContactPhoneSchema).max(CONTACT_PHONE_LIST_MAX_LENGTH);
47
79
 
@@ -101,6 +133,7 @@ export const SettingsSchema = z.object({
101
133
  phoneSales: z.string().nullable(),
102
134
  phoneWhatsappSales: z.string().nullable(),
103
135
  telegramChannel: z.string().nullable(),
136
+ maxChannel: z.string().nullable(),
104
137
  contactPhoneList: ContactPhoneListSchema.nullable(),
105
138
  menuItemsSort: z.array(z.nativeEnum(MenuItemsUrlEnum)).nullable(),
106
139
  phoneCountryCode: z.array(z.string()),
@@ -193,6 +226,7 @@ export const CreateSettingsSchema = z.object({
193
226
  phoneSales: z.string().nullable().optional(),
194
227
  phoneWhatsappSales: z.string().nullable().optional(),
195
228
  telegramChannel: z.string().nullable().optional(),
229
+ maxChannel: z.string().nullable().optional(),
196
230
  contactPhoneList: ContactPhoneListSchema.nullable().optional(),
197
231
  menuItemsSort: z.array(z.nativeEnum(MenuItemsUrlEnum)).nullable().optional(),
198
232
  phoneCountryCode: z.array(z.string()).optional(),
@@ -259,6 +293,7 @@ export const UpdateSettingsRequestSchema = z.object({
259
293
  phoneSales: z.string().nullable().optional(),
260
294
  phoneWhatsappSales: z.string().nullable().optional(),
261
295
  telegramChannel: z.string().nullable().optional(),
296
+ maxChannel: z.string().nullable().optional(),
262
297
  contactPhoneList: ContactPhoneListSchema.nullable().optional(),
263
298
  menuItemsSort: z.array(z.nativeEnum(MenuItemsUrlEnum)).nullable().optional(),
264
299
  phoneCountryCode: z.array(z.string()).optional(),