@redneckz/wildless-cms-uni-blocks 0.14.911 → 0.14.913

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/bundle/api/LeadServiceAPI.d.ts +12 -3
  2. package/bundle/bundle.umd.js +716 -684
  3. package/bundle/bundle.umd.min.js +1 -1
  4. package/bundle/components/ApplicationForm/VerifyPhoneDialog.d.ts +10 -0
  5. package/bundle/components/ApplicationForm/useVerifyPhoneDialogSubmit.d.ts +18 -0
  6. package/bundle/components/TariffsTable/TariffsTableCell.d.ts +1 -1
  7. package/bundle/retail/api/checkCode.d.ts +1 -9
  8. package/bundle/retail/api/sendCode.d.ts +1 -1
  9. package/bundle/retail/components/VerifyPhoneDialog/VerifyPhoneDialog.d.ts +0 -4
  10. package/bundle/retail/components/VerifyPhoneDialog/useVerifyPhoneDialogSubmit.d.ts +1 -6
  11. package/bundle/ui-kit/ResponseTypeDialog/ResponseTypeDialog.d.ts +1 -1
  12. package/bundle/ui-kit/VerifyPhoneDialogLayout/VerfiyPhoneDialogLayout.d.ts +16 -0
  13. package/bundle/utils/getTimer.d.ts +2 -0
  14. package/dist/api/LeadServiceAPI.d.ts +12 -3
  15. package/dist/api/LeadServiceAPI.js +0 -3
  16. package/dist/api/LeadServiceAPI.js.map +1 -1
  17. package/dist/components/ApplicationForm/ApplicationForm.js +1 -3
  18. package/dist/components/ApplicationForm/ApplicationForm.js.map +1 -1
  19. package/dist/components/ApplicationForm/VerifyPhoneDialog.d.ts +10 -0
  20. package/dist/components/ApplicationForm/VerifyPhoneDialog.js +37 -0
  21. package/dist/components/ApplicationForm/VerifyPhoneDialog.js.map +1 -0
  22. package/dist/components/ApplicationForm/useVerifyPhoneDialogSubmit.d.ts +18 -0
  23. package/dist/components/ApplicationForm/useVerifyPhoneDialogSubmit.js +50 -0
  24. package/dist/components/ApplicationForm/useVerifyPhoneDialogSubmit.js.map +1 -0
  25. package/dist/components/ExchangeRateTile/ExchangeCurrencyCalculator.js +2 -2
  26. package/dist/components/ExchangeRateTile/ExchangeCurrencyCalculator.js.map +1 -1
  27. package/dist/components/TariffsTable/TariffsTableCell.d.ts +1 -1
  28. package/dist/retail/api/checkCode.d.ts +1 -9
  29. package/dist/retail/api/checkCode.js +1 -22
  30. package/dist/retail/api/checkCode.js.map +1 -1
  31. package/dist/retail/api/sendCode.d.ts +1 -1
  32. package/dist/retail/api/sendCode.js +2 -8
  33. package/dist/retail/api/sendCode.js.map +1 -1
  34. package/dist/retail/components/VerifyPhoneDialog/VerifyPhoneDialog.d.ts +0 -4
  35. package/dist/retail/components/VerifyPhoneDialog/VerifyPhoneDialog.js +5 -24
  36. package/dist/retail/components/VerifyPhoneDialog/VerifyPhoneDialog.js.map +1 -1
  37. package/dist/retail/components/VerifyPhoneDialog/useVerifyPhoneDialogSubmit.d.ts +1 -6
  38. package/dist/retail/components/VerifyPhoneDialog/useVerifyPhoneDialogSubmit.js +7 -10
  39. package/dist/retail/components/VerifyPhoneDialog/useVerifyPhoneDialogSubmit.js.map +1 -1
  40. package/dist/retail/utils/mockLocalStorage.js +2 -2
  41. package/dist/retail/utils/mockLocalStorage.js.map +1 -1
  42. package/dist/ui-kit/ResponseTypeDialog/ResponseTypeDialog.d.ts +1 -1
  43. package/dist/ui-kit/VerifyPhoneDialogLayout/VerfiyPhoneDialogLayout.d.ts +16 -0
  44. package/dist/ui-kit/VerifyPhoneDialogLayout/VerfiyPhoneDialogLayout.js +17 -0
  45. package/dist/ui-kit/VerifyPhoneDialogLayout/VerfiyPhoneDialogLayout.js.map +1 -0
  46. package/dist/utils/getTimer.d.ts +2 -0
  47. package/dist/utils/getTimer.js +6 -0
  48. package/dist/utils/getTimer.js.map +1 -0
  49. package/lib/api/LeadServiceAPI.d.ts +12 -3
  50. package/lib/api/LeadServiceAPI.js +0 -3
  51. package/lib/api/LeadServiceAPI.js.map +1 -1
  52. package/lib/common.css +1 -1
  53. package/lib/components/ApplicationForm/ApplicationForm.js +1 -3
  54. package/lib/components/ApplicationForm/ApplicationForm.js.map +1 -1
  55. package/lib/components/ApplicationForm/VerifyPhoneDialog.d.ts +10 -0
  56. package/lib/components/ApplicationForm/VerifyPhoneDialog.js +35 -0
  57. package/lib/components/ApplicationForm/VerifyPhoneDialog.js.map +1 -0
  58. package/lib/components/ApplicationForm/useVerifyPhoneDialogSubmit.d.ts +18 -0
  59. package/lib/components/ApplicationForm/useVerifyPhoneDialogSubmit.js +47 -0
  60. package/lib/components/ApplicationForm/useVerifyPhoneDialogSubmit.js.map +1 -0
  61. package/lib/components/ExchangeRateTile/ExchangeCurrencyCalculator.js +2 -2
  62. package/lib/components/ExchangeRateTile/ExchangeCurrencyCalculator.js.map +1 -1
  63. package/lib/components/TariffsTable/TariffsTableCell.d.ts +1 -1
  64. package/lib/retail/api/checkCode.d.ts +1 -9
  65. package/lib/retail/api/checkCode.js +1 -22
  66. package/lib/retail/api/checkCode.js.map +1 -1
  67. package/lib/retail/api/sendCode.d.ts +1 -1
  68. package/lib/retail/api/sendCode.js +1 -7
  69. package/lib/retail/api/sendCode.js.map +1 -1
  70. package/lib/retail/components/VerifyPhoneDialog/VerifyPhoneDialog.d.ts +0 -4
  71. package/lib/retail/components/VerifyPhoneDialog/VerifyPhoneDialog.js +5 -24
  72. package/lib/retail/components/VerifyPhoneDialog/VerifyPhoneDialog.js.map +1 -1
  73. package/lib/retail/components/VerifyPhoneDialog/useVerifyPhoneDialogSubmit.d.ts +1 -6
  74. package/lib/retail/components/VerifyPhoneDialog/useVerifyPhoneDialogSubmit.js +5 -8
  75. package/lib/retail/components/VerifyPhoneDialog/useVerifyPhoneDialogSubmit.js.map +1 -1
  76. package/lib/retail/utils/mockLocalStorage.js +2 -2
  77. package/lib/retail/utils/mockLocalStorage.js.map +1 -1
  78. package/lib/ui-kit/ResponseTypeDialog/ResponseTypeDialog.d.ts +1 -1
  79. package/lib/ui-kit/VerifyPhoneDialogLayout/VerfiyPhoneDialogLayout.d.ts +16 -0
  80. package/lib/ui-kit/VerifyPhoneDialogLayout/VerfiyPhoneDialogLayout.js +15 -0
  81. package/lib/ui-kit/VerifyPhoneDialogLayout/VerfiyPhoneDialogLayout.js.map +1 -0
  82. package/lib/utils/getTimer.d.ts +2 -0
  83. package/lib/utils/getTimer.js +3 -0
  84. package/lib/utils/getTimer.js.map +1 -0
  85. package/mobile/bundle/api/LeadServiceAPI.d.ts +12 -3
  86. package/mobile/bundle/bundle.umd.js +716 -684
  87. package/mobile/bundle/bundle.umd.min.js +1 -1
  88. package/mobile/bundle/components/ApplicationForm/VerifyPhoneDialog.d.ts +10 -0
  89. package/mobile/bundle/components/ApplicationForm/useVerifyPhoneDialogSubmit.d.ts +18 -0
  90. package/mobile/bundle/components/TariffsTable/TariffsTableCell.d.ts +1 -1
  91. package/mobile/bundle/retail/api/checkCode.d.ts +1 -9
  92. package/mobile/bundle/retail/api/sendCode.d.ts +1 -1
  93. package/mobile/bundle/retail/components/VerifyPhoneDialog/VerifyPhoneDialog.d.ts +0 -4
  94. package/mobile/bundle/retail/components/VerifyPhoneDialog/useVerifyPhoneDialogSubmit.d.ts +1 -6
  95. package/mobile/bundle/ui-kit/ResponseTypeDialog/ResponseTypeDialog.d.ts +1 -1
  96. package/mobile/bundle/ui-kit/VerifyPhoneDialogLayout/VerfiyPhoneDialogLayout.d.ts +16 -0
  97. package/mobile/bundle/utils/getTimer.d.ts +2 -0
  98. package/mobile/dist/api/LeadServiceAPI.d.ts +12 -3
  99. package/mobile/dist/api/LeadServiceAPI.js +0 -3
  100. package/mobile/dist/api/LeadServiceAPI.js.map +1 -1
  101. package/mobile/dist/components/ApplicationForm/ApplicationForm.js +1 -3
  102. package/mobile/dist/components/ApplicationForm/ApplicationForm.js.map +1 -1
  103. package/mobile/dist/components/ApplicationForm/VerifyPhoneDialog.d.ts +10 -0
  104. package/mobile/dist/components/ApplicationForm/VerifyPhoneDialog.js +37 -0
  105. package/mobile/dist/components/ApplicationForm/VerifyPhoneDialog.js.map +1 -0
  106. package/mobile/dist/components/ApplicationForm/useVerifyPhoneDialogSubmit.d.ts +18 -0
  107. package/mobile/dist/components/ApplicationForm/useVerifyPhoneDialogSubmit.js +50 -0
  108. package/mobile/dist/components/ApplicationForm/useVerifyPhoneDialogSubmit.js.map +1 -0
  109. package/mobile/dist/components/ExchangeRateTile/ExchangeCurrencyCalculator.js +2 -2
  110. package/mobile/dist/components/ExchangeRateTile/ExchangeCurrencyCalculator.js.map +1 -1
  111. package/mobile/dist/components/TariffsTable/TariffsTableCell.d.ts +1 -1
  112. package/mobile/dist/retail/api/checkCode.d.ts +1 -9
  113. package/mobile/dist/retail/api/checkCode.js +1 -22
  114. package/mobile/dist/retail/api/checkCode.js.map +1 -1
  115. package/mobile/dist/retail/api/sendCode.d.ts +1 -1
  116. package/mobile/dist/retail/api/sendCode.js +2 -8
  117. package/mobile/dist/retail/api/sendCode.js.map +1 -1
  118. package/mobile/dist/retail/components/VerifyPhoneDialog/VerifyPhoneDialog.d.ts +0 -4
  119. package/mobile/dist/retail/components/VerifyPhoneDialog/VerifyPhoneDialog.js +5 -24
  120. package/mobile/dist/retail/components/VerifyPhoneDialog/VerifyPhoneDialog.js.map +1 -1
  121. package/mobile/dist/retail/components/VerifyPhoneDialog/useVerifyPhoneDialogSubmit.d.ts +1 -6
  122. package/mobile/dist/retail/components/VerifyPhoneDialog/useVerifyPhoneDialogSubmit.js +7 -10
  123. package/mobile/dist/retail/components/VerifyPhoneDialog/useVerifyPhoneDialogSubmit.js.map +1 -1
  124. package/mobile/dist/retail/utils/mockLocalStorage.js +2 -2
  125. package/mobile/dist/retail/utils/mockLocalStorage.js.map +1 -1
  126. package/mobile/dist/ui-kit/ResponseTypeDialog/ResponseTypeDialog.d.ts +1 -1
  127. package/mobile/dist/ui-kit/VerifyPhoneDialogLayout/VerfiyPhoneDialogLayout.d.ts +16 -0
  128. package/mobile/dist/ui-kit/VerifyPhoneDialogLayout/VerfiyPhoneDialogLayout.js +17 -0
  129. package/mobile/dist/ui-kit/VerifyPhoneDialogLayout/VerfiyPhoneDialogLayout.js.map +1 -0
  130. package/mobile/dist/utils/getTimer.d.ts +2 -0
  131. package/mobile/dist/utils/getTimer.js +6 -0
  132. package/mobile/dist/utils/getTimer.js.map +1 -0
  133. package/mobile/lib/api/LeadServiceAPI.d.ts +12 -3
  134. package/mobile/lib/api/LeadServiceAPI.js +0 -3
  135. package/mobile/lib/api/LeadServiceAPI.js.map +1 -1
  136. package/mobile/lib/common.css +1 -1
  137. package/mobile/lib/components/ApplicationForm/ApplicationForm.js +1 -3
  138. package/mobile/lib/components/ApplicationForm/ApplicationForm.js.map +1 -1
  139. package/mobile/lib/components/ApplicationForm/VerifyPhoneDialog.d.ts +10 -0
  140. package/mobile/lib/components/ApplicationForm/VerifyPhoneDialog.js +35 -0
  141. package/mobile/lib/components/ApplicationForm/VerifyPhoneDialog.js.map +1 -0
  142. package/mobile/lib/components/ApplicationForm/useVerifyPhoneDialogSubmit.d.ts +18 -0
  143. package/mobile/lib/components/ApplicationForm/useVerifyPhoneDialogSubmit.js +47 -0
  144. package/mobile/lib/components/ApplicationForm/useVerifyPhoneDialogSubmit.js.map +1 -0
  145. package/mobile/lib/components/ExchangeRateTile/ExchangeCurrencyCalculator.js +2 -2
  146. package/mobile/lib/components/ExchangeRateTile/ExchangeCurrencyCalculator.js.map +1 -1
  147. package/mobile/lib/components/TariffsTable/TariffsTableCell.d.ts +1 -1
  148. package/mobile/lib/retail/api/checkCode.d.ts +1 -9
  149. package/mobile/lib/retail/api/checkCode.js +1 -22
  150. package/mobile/lib/retail/api/checkCode.js.map +1 -1
  151. package/mobile/lib/retail/api/sendCode.d.ts +1 -1
  152. package/mobile/lib/retail/api/sendCode.js +1 -7
  153. package/mobile/lib/retail/api/sendCode.js.map +1 -1
  154. package/mobile/lib/retail/components/VerifyPhoneDialog/VerifyPhoneDialog.d.ts +0 -4
  155. package/mobile/lib/retail/components/VerifyPhoneDialog/VerifyPhoneDialog.js +5 -24
  156. package/mobile/lib/retail/components/VerifyPhoneDialog/VerifyPhoneDialog.js.map +1 -1
  157. package/mobile/lib/retail/components/VerifyPhoneDialog/useVerifyPhoneDialogSubmit.d.ts +1 -6
  158. package/mobile/lib/retail/components/VerifyPhoneDialog/useVerifyPhoneDialogSubmit.js +5 -8
  159. package/mobile/lib/retail/components/VerifyPhoneDialog/useVerifyPhoneDialogSubmit.js.map +1 -1
  160. package/mobile/lib/retail/utils/mockLocalStorage.js +2 -2
  161. package/mobile/lib/retail/utils/mockLocalStorage.js.map +1 -1
  162. package/mobile/lib/ui-kit/ResponseTypeDialog/ResponseTypeDialog.d.ts +1 -1
  163. package/mobile/lib/ui-kit/VerifyPhoneDialogLayout/VerfiyPhoneDialogLayout.d.ts +16 -0
  164. package/mobile/lib/ui-kit/VerifyPhoneDialogLayout/VerfiyPhoneDialogLayout.js +15 -0
  165. package/mobile/lib/ui-kit/VerifyPhoneDialogLayout/VerfiyPhoneDialogLayout.js.map +1 -0
  166. package/mobile/lib/utils/getTimer.d.ts +2 -0
  167. package/mobile/lib/utils/getTimer.js +3 -0
  168. package/mobile/lib/utils/getTimer.js.map +1 -0
  169. package/mobile/src/api/LeadServiceAPI.ts +13 -7
  170. package/mobile/src/components/ApplicationForm/ApplicationForm.tsx +1 -3
  171. package/mobile/src/components/ApplicationForm/VerifyPhoneDialog.tsx +73 -0
  172. package/mobile/src/components/ApplicationForm/useVerifyPhoneDialogSubmit.tsx +63 -0
  173. package/mobile/src/components/ExchangeRateTile/ExchangeCurrencyCalculator.tsx +2 -2
  174. package/mobile/src/components/TariffsTable/TariffsTableCell.tsx +1 -1
  175. package/mobile/src/retail/api/checkCode.ts +2 -47
  176. package/mobile/src/retail/api/sendCode.ts +1 -9
  177. package/mobile/src/retail/components/VerifyPhoneDialog/VerifyPhoneDialog.tsx +23 -82
  178. package/mobile/src/retail/components/VerifyPhoneDialog/useVerifyPhoneDialogSubmit.tsx +7 -21
  179. package/mobile/src/retail/utils/mockLocalStorage.ts +9 -15
  180. package/mobile/src/ui-kit/ResponseTypeDialog/ResponseTypeDialog.tsx +1 -1
  181. package/mobile/src/ui-kit/VerifyPhoneDialogLayout/VerfiyPhoneDialogLayout.tsx +80 -0
  182. package/mobile/src/utils/getTimer.ts +4 -0
  183. package/package.json +1 -1
  184. package/src/api/LeadServiceAPI.ts +13 -7
  185. package/src/components/ApplicationForm/ApplicationForm.tsx +1 -3
  186. package/src/components/ApplicationForm/VerifyPhoneDialog.tsx +73 -0
  187. package/src/components/ApplicationForm/useVerifyPhoneDialogSubmit.tsx +63 -0
  188. package/src/components/ExchangeRateTile/ExchangeCurrencyCalculator.tsx +2 -2
  189. package/src/components/TariffsTable/TariffsTableCell.tsx +1 -1
  190. package/src/retail/api/checkCode.ts +2 -47
  191. package/src/retail/api/sendCode.ts +1 -9
  192. package/src/retail/components/VerifyPhoneDialog/VerifyPhoneDialog.tsx +23 -82
  193. package/src/retail/components/VerifyPhoneDialog/useVerifyPhoneDialogSubmit.tsx +7 -21
  194. package/src/retail/utils/mockLocalStorage.ts +9 -15
  195. package/src/ui-kit/ResponseTypeDialog/ResponseTypeDialog.tsx +1 -1
  196. package/src/ui-kit/VerifyPhoneDialogLayout/VerfiyPhoneDialogLayout.tsx +80 -0
  197. package/src/utils/getTimer.ts +4 -0
  198. package/bundle/model/onClose.d.ts +0 -3
  199. package/dist/model/onClose.d.ts +0 -3
  200. package/dist/model/onClose.js +0 -2
  201. package/dist/model/onClose.js.map +0 -1
  202. package/lib/model/onClose.d.ts +0 -3
  203. package/lib/model/onClose.js +0 -2
  204. package/lib/model/onClose.js.map +0 -1
  205. package/mobile/bundle/model/onClose.d.ts +0 -3
  206. package/mobile/dist/model/onClose.d.ts +0 -3
  207. package/mobile/dist/model/onClose.js +0 -2
  208. package/mobile/dist/model/onClose.js.map +0 -1
  209. package/mobile/lib/model/onClose.d.ts +0 -3
  210. package/mobile/lib/model/onClose.js +0 -2
  211. package/mobile/lib/model/onClose.js.map +0 -1
  212. package/mobile/src/model/onClose.ts +0 -3
  213. package/src/model/onClose.ts +0 -3
@@ -1110,9 +1110,6 @@
1110
1110
  };
1111
1111
  try {
1112
1112
  const response = await LeadServiceFetch(`${API_BASE_URI$1}/confirmCorporateLead`, submitBody);
1113
- if (!response?.ok) {
1114
- return null;
1115
- }
1116
1113
  return await response.json();
1117
1114
  }
1118
1115
  catch (e) {
@@ -1247,630 +1244,90 @@
1247
1244
  setFormStateUnsafe(normalizer);
1248
1245
  }
1249
1246
  }, [normalizer]);
1250
- return [formState, setFormState];
1251
- }
1252
-
1253
- function useForm(initialState, { resetOnSubmit, formValidator, normalizer, onChange, onSubmit } = {}) {
1254
- const [formState, setFormState] = useNormalizedFormState(initialState, normalizer, onChange);
1255
- const fieldRefs = useRef(getRefsObject(initialState));
1256
- const [isDirtyForm, { setTrue: markAsDirty, setFalse: markAsClean }] = useBool(false);
1257
- const dirtyFieldsMap = useRef({});
1258
- const [fieldValidatorsMap, { isValid, errors }] = useFormValidator(formState, formValidator);
1259
- const field = useCallback((fieldName, options = {}) => {
1260
- const { parse, format, onChange: onFieldChange } = options;
1261
- const value = formState[fieldName];
1262
- const isDirty = isDirtyForm || dirtyFieldsMap.current?.[String(fieldName)];
1263
- const fieldValidator = options?.validator ?? fieldValidatorsMap[fieldName];
1264
- const fieldErrors = isDirty && fieldValidator ? fieldValidator(value) : [];
1265
- return {
1266
- setFieldRef: (_) => {
1267
- if (fieldRefs.current) {
1268
- fieldRefs.current[fieldName] = _;
1269
- }
1270
- },
1271
- fieldRef: fieldRefs.current?.[fieldName],
1272
- value: format ? format(value) : value,
1273
- isDirty,
1274
- errors: fieldValidator && fieldErrors,
1275
- error: fieldErrors[0],
1276
- onChange: (_) => {
1277
- dirtyFieldsMap.current ||= {};
1278
- dirtyFieldsMap.current[String(fieldName)] = true;
1279
- const fieldVal = parse ? parse(_) : _;
1280
- onFieldChange?.(fieldVal);
1281
- setFormState((prev) => ({ ...prev, [fieldName]: fieldVal }));
1282
- },
1283
- };
1284
- }, [formState, isDirtyForm, fieldValidatorsMap, setFormState]);
1285
- const update = useCallback((_) => {
1286
- dirtyFieldsMap.current = _;
1287
- setFormState(_);
1288
- }, [setFormState]);
1289
- const reset = useCallback(() => {
1290
- dirtyFieldsMap.current = {};
1291
- markAsClean();
1292
- setFormState(initialState);
1293
- }, [initialState, setFormState]);
1294
- const handleSubmit = useCallback((ev) => {
1295
- ev.preventDefault();
1296
- if (isValid) {
1297
- resetOnSubmit && reset();
1298
- onSubmit?.(formState, ev);
1299
- }
1300
- else {
1301
- const errorFieldName = getErrorFieldName(formState, fieldValidatorsMap);
1302
- markAsDirty();
1303
- field(errorFieldName).fieldRef?.scrollIntoView({ behavior: 'smooth' });
1304
- }
1305
- }, [resetOnSubmit, formState, isValid, reset, onSubmit]);
1306
- return [formState, { errors, field, update, reset, onSubmit: handleSubmit }];
1307
- }
1308
- const getRefsObject = (initialState) => Object.keys(initialState).reduce((acc, key) => ({ ...acc, [key]: null }), {});
1309
- const getErrorFieldName = (formState, fieldValidatorsMap = {}) => {
1310
- const [errorFieldName = ''] = Object.entries(formState).find(([fieldName, value]) => {
1311
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
1312
- return fieldValidatorsMap[fieldName]?.(value)?.length;
1313
- }) ?? [];
1314
- return errorFieldName;
1315
- };
1316
-
1317
- function copy(source, target) {
1318
- for (const [k, v] of source.entries()) {
1319
- if (v !== null && v !== undefined) {
1320
- target.setItem(k, v);
1321
- }
1322
- else {
1323
- target.removeItem(k);
1324
- }
1325
- }
1326
- }
1327
-
1328
- function replicate(primary, secondary) {
1329
- copy(primary, secondary);
1330
- copy(secondary, primary);
1331
- return primary.bus.watch(({ type, event }) => {
1332
- if (event !== null && event !== undefined) {
1333
- secondary.setItem(type, event);
1334
- }
1335
- else {
1336
- secondary.removeItem(type);
1337
- }
1338
- });
1339
- }
1340
-
1341
- class StorageAdapter {
1342
- storage;
1343
- bus;
1344
- get size() {
1345
- return this.storage?.length ?? 0;
1346
- }
1347
- constructor(storage, bus = new EventBus()) {
1348
- this.storage = storage;
1349
- this.bus = bus;
1350
- }
1351
- hasItem(key) {
1352
- return Boolean(this.storage?.getItem(String(key)));
1353
- }
1354
- getItem(key) {
1355
- const _ = this.storage?.getItem(String(key)) ?? null;
1356
- try {
1357
- return JSON.parse(String(_));
1358
- }
1359
- catch (ex) {
1360
- return null;
1361
- }
1362
- }
1363
- entries() {
1364
- return Array.from({ length: this.size }, (_, i) => {
1365
- const k = String(this.storage?.key(i));
1366
- return [k, this.getItem(k)];
1367
- });
1368
- }
1369
- setItem(key, value) {
1370
- if (value !== null) {
1371
- this.storage?.setItem(String(key), JSON.stringify(value));
1372
- }
1373
- else {
1374
- this.storage?.removeItem(String(key));
1375
- }
1376
- this.bus?.subject(key, value);
1377
- }
1378
- removeItem(key) {
1379
- this.storage?.removeItem(String(key));
1380
- this.bus?.subject(key, null);
1381
- }
1382
- }
1383
-
1384
- class Store {
1385
- bus;
1386
- store = new Map();
1387
- get size() {
1388
- return this.store.size;
1389
- }
1390
- constructor(bus = new EventBus()) {
1391
- this.bus = bus;
1392
- }
1393
- hasItem(key) {
1394
- return this.store.has(key);
1395
- }
1396
- getItem(key) {
1397
- return this.store.get(key);
1398
- }
1399
- entries() {
1400
- return this.store.entries();
1401
- }
1402
- setItem(key, value) {
1403
- this.store.set(key, value);
1404
- this.bus.subject(key, value);
1405
- }
1406
- removeItem(key) {
1407
- this.store.delete(key);
1408
- this.bus.subject(key, null);
1409
- }
1410
- }
1411
-
1412
- function useRerender() {
1413
- const [, setCount] = useState(0);
1414
- return useCallback(() => setCount(_ => (_ + 1) % (1 << 16)), []);
1415
- }
1416
-
1417
- const DEFAULT_METHODS = {};
1418
- /**
1419
- * MobX like reactivity (simplified).
1420
- * Can be used to migrate from Redux/MobX or something else
1421
- *
1422
- * @param store
1423
- * @returns reactive proxy backed by store
1424
- */
1425
- function useStore(store, methods = DEFAULT_METHODS) {
1426
- const deps = useRef(null);
1427
- const render = useRerender();
1428
- useEffect(() => store.bus.watch(ev => {
1429
- if (deps.current?.has(String(ev.type))) {
1430
- render();
1431
- }
1432
- }), [store, render]);
1433
- return useMemo(() => new Proxy(methods, {
1434
- get(_, key) {
1435
- deps.current ||= new Set();
1436
- deps.current.add(key);
1437
- return store.getItem(key);
1438
- },
1439
- has(_, key) {
1440
- deps.current ||= new Set();
1441
- deps.current.add(key);
1442
- return store.hasItem(key);
1443
- },
1444
- set(_, key, value) {
1445
- store.setItem(key, value);
1446
- return true;
1447
- },
1448
- deleteProperty(_, key) {
1449
- store.removeItem(key);
1450
- return true;
1451
- }
1452
- }), [store]);
1453
- }
1454
-
1455
- const sessionStore = new Store(); // sessionStorage cache
1456
- replicate(sessionStore, new StorageAdapter(globalThis?.sessionStorage));
1457
- function useSessionStore() {
1458
- return useStore(sessionStore);
1459
- }
1460
-
1461
- const noop = () => {
1462
- // Do nothing
1463
- };
1464
-
1465
- const themeStyle$1 = {
1466
- primary: style('text-white bg-primary-main hover:bg-primary-hover active:bg-primary-active', 'group-data-secondary:text-primary-main group-data-secondary:bg-white', 'group-data-secondary:hover:text-white group-data-secondary:hover:bg-primary-hover', 'group-data-secondary:active:bg-primary-active'),
1467
- secondary: style('text-primary-main bg-main-divider hover:text-white hover:bg-primary-hover active:bg-primary-active', 'group-data-secondary:text-white group-data-secondary:bg-white/20', 'group-data-secondary:hover:bg-primary-hover', 'group-data-secondary:active:bg-primary-active'),
1468
- };
1469
- const embeddedStyle = style('group/btn-embedded', 'bg-transparent border border-transparent outline-none');
1470
- const disabledStyle = style('bg-main-gray text-main-disabled cursor-not-allowed');
1471
- const Button = JSX(({ className, type = 'button', version = 'primary', shape = 'default', embedded, disabled, role, ariaLabel, data, dataTheme, children, wcmsIgnore, onClick = noop, }) => {
1472
- const handleClick = useCallback(role !== 'tab' ? handlerDecorator(onClick) : onClick, [
1473
- role,
1474
- onClick,
1475
- ]);
1476
- const aspectsAttrs = useMemo(() => getAspectsAttributes(data), [data]);
1477
- const isRound = shape === 'round';
1478
- return (jsx("button", { className: style('font-sans flex items-center gap-xs', {
1479
- [themeStyle$1[version]]: !disabled && !embedded,
1480
- [embeddedStyle]: embedded,
1481
- [disabledStyle]: disabled,
1482
- }, embedded ? 'justify-between' : 'justify-center', embedded || isRound ? 'p-0' : 'px-9 py-4', {
1483
- 'rounded-md': shape === 'default',
1484
- 'rounded-full': isRound,
1485
- }, className), type: type, role: role, "aria-label": ariaLabel, disabled: disabled, "aria-disabled": disabled ? 'true' : undefined, "data-theme": dataTheme, "data-wcms-ignore": wcmsIgnore, ...aspectsAttrs, onClick: handleClick, children: children }));
1486
- });
1487
-
1488
- const ButtonTitle = JSX(({ className, children }) => (jsx("span", { className: style('inline-flex items-center text-start gap-s group-[]/btn-embedded:text-primary-main', className), children: children })));
1489
-
1490
- const CloseButton = JSX(({ className, onClose }) => (jsx("button", { className: style('flex justify-center items-center w-12 h-12 p-2xs bg-transparent border-none', className), onClick: onClose, title: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C", type: "button", children: jsx(Icon, { name: "CloseIcon", width: "20", height: "20", iconVersion: "gray" }) })));
1491
-
1492
- const DIALOG_STYLE = {
1493
- sm: 'max-w-sm top-1/3',
1494
- lg: 'max-w-lg',
1495
- '4xl': 'max-w-4xl',
1496
- none: 'mt-0',
1497
- };
1498
- const Dialog = JSX(({ head, maxWidth = '4xl', children, onClose, onClick }) => (jsxs("div", { className: style('relative bg-white p-lg pb-6xl my-6xl mx-auto rounded-lg w-full', DIALOG_STYLE[maxWidth]), role: "dialog", title: "\u0414\u0438\u0430\u043B\u043E\u0433", onClick: onClick, children: [jsxs("div", { className: "sticky py-xl top-0 bg-white z-10", children: [jsx(CloseButton, { className: "absolute top-0 right-0 z-10", onClose: onClose }), jsx("div", { className: "container", children: head })] }), jsx("div", { className: "container", children: children })] })));
1499
-
1500
- function useDialog(Dialog, initialProps = {}) {
1501
- const { open, close, ...rest } = useDialogManager();
1502
- const openDialog = useCallback((props, options = {}) => open({
1503
- dialog: (jsx(Dialog, { ...initialProps, ...props, onClose: () => {
1504
- close();
1505
- props.onClose?.();
1506
- } })),
1507
- ...options,
1508
- }), [Dialog, open, close]);
1509
- return { open: openDialog, close, ...rest };
1510
- }
1511
-
1512
- const Loader = JSX(({ color = 'text-primary-main', position = 'absolute', blur = true, size = 'big' }) => (jsx("div", { className: style('flex justify-center items-center h-full w-full z-50', position, {
1513
- 'backdrop-blur': blur,
1514
- }), children: jsx("div", { className: style('inline-block', 'animate-spin rounded-full', 'border-solid border-current', 'border-r-transparent', size === 'extraSmall' && 'border-2 h-4 w-4', size === 'small' && 'border-4 h-8 w-8', size === 'big' && 'border-8 h-28 w-28', color), role: "status" }) })));
1515
-
1516
- const Timer = JSX(({ className, seconds }) => (jsx("span", { className: className, children: formatTimer(seconds) })));
1517
- const formatTimer = (seconds) => {
1518
- const minutes = Math.floor(seconds / 60);
1519
- return [minutes, seconds % 60].map((_) => String(_).padStart(2, '0')).join(':');
1520
- };
1521
-
1522
- const useInterval = (handler, period) => {
1523
- const timer = useRef(null);
1524
- const stop = useCallback(() => clearInterval(timer.current), []);
1525
- const start = useCallback(() => {
1526
- stop();
1527
- timer.current = setInterval(() => handler(stop), period);
1528
- }, [handler, period, stop]);
1529
- useEffect(() => {
1530
- start();
1531
- return stop;
1532
- }, [start, stop]);
1533
- return { start, stop };
1534
- };
1535
-
1536
- function useCountDownTimer({ seconds, period = 1000, onTick, onEnd }) {
1537
- const counter = useRef(seconds);
1538
- const handleTick = useCallback((stop) => {
1539
- counter.current ||= 0;
1540
- counter.current = Math.max(0, counter.current - 1);
1541
- try {
1542
- onTick?.(counter.current);
1543
- }
1544
- finally {
1545
- if (counter.current <= 0) {
1546
- stop();
1547
- onEnd?.();
1548
- }
1549
- }
1550
- }, [onTick, onEnd]);
1551
- const { start } = useInterval(handleTick, period);
1552
- return useCallback((_) => {
1553
- counter.current = _;
1554
- start();
1555
- }, []);
1556
- }
1557
-
1558
- const getTraceId = () => {
1559
- const result = new Uint8Array(8);
1560
- globalThis.crypto.getRandomValues(result);
1561
- return result.reduce((acc, _) => `${acc}${_.toString(16).padStart(2, '0')}`, '');
1562
- };
1563
-
1564
- const fetchRetailJSON = async (url, method, body) => {
1565
- try {
1566
- const response = await doRequest(url, method, body);
1567
- return response.json();
1568
- }
1569
- catch (err) {
1570
- console.error(err);
1571
- return null;
1572
- }
1573
- };
1574
- async function doRequest(url, method, body) {
1575
- const traceId = getTraceId();
1576
- return globalThis?.fetch?.(`${RETAIL_API_BASE_URI}${url}`, {
1577
- method,
1578
- headers: {
1579
- 'Content-Type': 'application/json',
1580
- 'X-B3-Sampled': '1',
1581
- 'X-B3-Spanid': traceId,
1582
- 'X-B3-Traceid': traceId,
1583
- ...getAuthorizationHeaders(),
1584
- },
1585
- credentials: 'include',
1586
- body: body ? JSON.stringify(body) : null,
1587
- });
1588
- }
1589
- const getAuthorizationHeaders = () => {
1590
- const token = sessionStorage.getItem('accessToken');
1591
- return token ? { Authorization: `Bearer ${token}` } : null;
1592
- };
1593
-
1594
- const API$2 = LeadServiceAPI();
1595
- const sendCode = (body, isRetail) => {
1596
- return isRetail ? fetchRetail(body) : fetchMain$1(body);
1597
- };
1598
- const fetchRetail = (body) => doRequest('/sms/sendCode', 'POST', body)
1599
- .then((res) => res.text())
1600
- .then((text) => text === 'OK');
1601
- const fetchMain$1 = (body) => API$2.sendCode({ phone: body.phoneNumber });
1602
-
1603
- const SubmitButton$1 = JSX(({ isLoading, disabled, children, className, ...rest }) => (jsxs(Button, { type: "submit", className: style('relative', className), disabled: isLoading || disabled, ...rest, children: [isLoading ? jsx(Loader, { blur: true, size: "small" }) : null, children] })));
1604
-
1605
- const inputValidStyle = 'border border-solid outline-none border-gray hover:border-primary-hover active:border-primary-text focus:border-primary-text rounded';
1606
-
1607
- const getValidStyle = (valid) => (valid ? inputValidStyle : 'border-error');
1608
-
1609
- const renderLabel$1 = (label) => label ? (jsx(Text, { size: "text-m", color: "text-primary-text", font: "font-light", children: label })) : null;
1610
-
1611
- const Input = JSX(
1612
- // eslint-disable-next-line max-lines-per-function
1613
- ({ key, className, id, name, type = 'text', label, placeholder, value = '', valid = true, pattern, autoFocus = false, isTextarea = false, disabled = false, children, onChange, onFocus, onBlur, }) => {
1614
- const inputRef = useRef(null);
1615
- const handleChange = useCallback((e) => {
1616
- const valueWithoutSpace = (e.target?.value ?? '').trimStart();
1617
- onChange && onChange(valueWithoutSpace);
1618
- }, [onChange]);
1619
- useEffect(() => {
1620
- if (autoFocus) {
1621
- inputRef.current?.focus();
1622
- }
1623
- }, [autoFocus, inputRef]);
1624
- const paddingStyle = children ? 'pr-3xl' : '';
1625
- const validStyle = getValidStyle(valid);
1626
- const ariaLabel = label ?? name ?? id;
1627
- return (jsxs("div", { className: style('relative', className), children: [jsxs("label", { className: "space-y-xs", children: [renderLabel$1(label), isTextarea ? (jsx("textarea", { className: style('block resize-y min-h-24', defaultStyle$1, validStyle), id: style('textarea', id), value: value, name: name || id, placeholder: placeholder, disabled: disabled, "aria-label": ariaLabel, onChange: handleChange, onFocus: onFocus, onBlur: onBlur }, key)) : (jsx("input", { ref: inputRef, className: style('h-14', defaultStyle$1, paddingStyle, validStyle), id: id, type: type, value: value, name: name || id, placeholder: placeholder, pattern: pattern, disabled: disabled, "aria-label": ariaLabel, onChange: handleChange, onFocus: onFocus, onBlur: onBlur }, key))] }), children] }));
1628
- });
1629
- const defaultStyle$1 = 'w-full border rounded-md text-primary-text outline-none p-m';
1630
-
1631
- const ICON_SIZE = { width: '103', height: '21' };
1632
-
1633
- const logoTitleSizeStyle = 'text-s';
1634
-
1635
- const ICON_VERSION_MAP = {
1636
- 'bg-white': 'color',
1637
- transparent: 'white',
1638
- };
1639
- const SVG_COLOR = {
1640
- 'bg-white': 'text-primary-main',
1641
- transparent: 'text-white',
1642
- };
1643
- const renderImage = (bgColor, image, size) => {
1644
- const img = image?.src
1645
- ? image
1646
- : {
1647
- icon: image?.icon || 'LogoIcon',
1648
- iconVersion: ICON_VERSION_MAP[bgColor],
1649
- };
1650
- return (jsx(Img, { image: img, className: SVG_COLOR[bgColor], width: size?.width, height: size?.height }));
1651
- };
1652
-
1653
- const TEXT_COLOR = {
1654
- 'bg-white': 'text-primary-text',
1655
- transparent: 'text-white',
1656
- };
1657
- const Logo = JSX(({ className, href = '/', logo, children, targetBlank, bgColor = 'bg-white', showTitle = true, data, }) => (jsxs("a", { className: style('inline-flex items-center font-sans no-underline', className), href: logo?.href ?? href, target: targetBlank ? '_blank' : '_self', "aria-label": logo?.title ?? 'Россельхозбанк', ...getAspectsAttributes(data), children: [renderImage(bgColor, logo?.image, ICON_SIZE), showTitle
1658
- ? children ?? (jsx("div", { className: "ml-s", children: jsx(Text, { font: "font-medium", color: TEXT_COLOR[bgColor], size: logoTitleSizeStyle, children: logo?.title ?? 'Россельхозбанк' }) }))
1659
- : null] })));
1660
-
1661
- const checkCaptcha = (body) => doRequest('/sms/checkCaptcha', 'POST', body)
1662
- .then((res) => res.text())
1663
- .then((text) => text !== 'ERROR');
1664
-
1665
- const createCaptcha = (phoneNumber) => doRequest(`/sms/createCaptcha?phoneNumber=${encodeURIComponent(phoneNumber)}`, 'GET').then(async (res) => (res ? res.blob() : new Blob()));
1666
-
1667
- const CaptchaDialog = JSX(({ phoneNumber, sendCode, onClose }) => {
1668
- const [captcha, setCaptcha] = useState('');
1669
- const [code, setCode] = useState('');
1670
- const [hasError, setHasError] = useState(false);
1671
- const [isLoading, { setTrue: startLoading, setFalse: endLoading }] = useBool(false);
1672
- const { closeAll } = useDialogManager();
1673
- const handleCheckCaptcha = useCallback(async () => {
1674
- startLoading();
1675
- const isValidCode = await checkCaptcha({ captchaText: code });
1676
- if (isValidCode) {
1677
- onClose?.();
1678
- sendCode?.();
1679
- }
1680
- else {
1681
- setHasError(true);
1682
- }
1683
- endLoading();
1684
- }, [code, sendCode]);
1685
- const handleCreateCaptcha = useCallback(() => {
1686
- (async () => setCaptcha(URL.createObjectURL(await createCaptcha(phoneNumber))))();
1687
- }, []);
1688
- useEffect(handleCreateCaptcha, []);
1689
- return (jsx(Dialog, { head: jsx(Logo, {}), onClose: onClose, children: jsxs("div", { className: "flex flex-col gap-lg items-center", children: [jsxs("div", { className: "flex", children: [jsx("img", { className: "grow", src: captcha }), jsx(Button, { className: "w-8", embedded: true, onClick: handleCreateCaptcha, children: jsx(Icon, { iconVersion: "normal", name: "RefreshIcon" }) })] }), jsx(Input, { className: "w-80", onChange: setCode, value: code, placeholder: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043A\u043E\u0434 \u0441 \u043A\u0430\u0440\u0442\u0438\u043D\u043A\u0438" }), hasError ? jsx("div", { className: "text-error", children: "\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u043A\u043E\u0434" }) : null, jsxs("div", { className: "flex w-80 justify-between", children: [jsx(Button, { version: "secondary", onClick: closeAll, children: "\u0412\u0435\u0440\u043D\u0443\u0442\u044C\u0441\u044F" }), jsx(SubmitButton$1, { version: "secondary", disabled: !code, onClick: handleCheckCaptcha, children: "\u041E\u0442\u043F\u0440\u0430\u0432\u0438\u0442\u044C" })] }), isLoading ? jsx(Loader, { blur: false }) : null] }) }));
1690
- });
1691
-
1692
- const InputCode = JSX(({ values, setValues, hasError, errorText }) => {
1693
- const [activeIndex, setActiveIndex] = useState(0);
1694
- const inputRefs = useRef([]);
1695
- useEffect(() => {
1696
- inputRefs.current?.[activeIndex]?.focus();
1697
- }, [activeIndex]);
1698
- const handleChange = useCallback((index) => (event) => {
1699
- const { value } = event.currentTarget;
1700
- const oneValue = value.slice(0, 1);
1701
- setValues(values.map((_, i) => (i === index ? oneValue : _)));
1702
- setActiveIndex(index + 1);
1703
- }, [values]);
1704
- const handleKeyDown = useCallback((currentIndex) => (event) => {
1705
- const { key } = event;
1706
- if (key === 'Backspace' && !values[currentIndex]) {
1707
- const previousIndex = currentIndex > 0 ? currentIndex - 1 : values.length - 1;
1708
- const updatedValues = values.map((value, index) => (index === previousIndex ? '' : value));
1709
- setValues(updatedValues);
1710
- setActiveIndex(previousIndex);
1711
- }
1712
- }, [values]);
1713
- const handlePaste = useCallback((event) => {
1714
- event.preventDefault();
1715
- const pastedData = event.clipboardData.getData('text');
1716
- const updatedValues = values.map((_, idx) => (idx < pastedData.length ? pastedData[idx] : _));
1717
- setValues(updatedValues);
1718
- setActiveIndex(updatedValues.length - 1);
1719
- }, [values]);
1720
- return (jsxs("div", { className: "flex flex-col gap-2 text-center", children: [jsx("div", { children: values.map((value, index) => (jsx("input", { type: "number", maxLength: 1, value: value, onChange: handleChange(index), onPaste: handlePaste, ref: (ref) => {
1721
- if (!inputRefs.current) {
1722
- inputRefs.current = [];
1723
- }
1724
- inputRefs.current[index] = ref;
1725
- }, onFocus: (event) => event.target.select(), onKeyDown: handleKeyDown(index), className: getInputStyle(index, values, hasError) }, index))) }), hasError ? jsx("div", { className: "text-error", children: errorText }) : null] }));
1726
- });
1727
- const getInputStyle = (index, values, hasError = false) => {
1728
- const isInputEmpty = !values[index];
1729
- return `w-16 sm:w-20 h-24 text-5xl text-center p-md m-2 border ${getValidStyle(!hasError || !isInputEmpty)} rounded-md caret-transparent outline-none`;
1730
- };
1731
-
1732
- const SubmitButton = JSX(({ disabled = false, onClick, text }) => (jsx(Button, { type: "button", onClick: onClick, disabled: disabled, children: jsx(Text, { font: "font-normal", children: text }) })));
1733
-
1734
- const API$1 = LeadServiceAPI();
1735
- const checkCode = async (body, isRetail) => {
1736
- const transformedBody = transformBody(body, isRetail);
1737
- return isRetail
1738
- ? fetchRetailJSON('/sms/checkCode', 'POST', transformedBody).then(saveToken)
1739
- : fetchMain(transformedBody).then((res) => {
1740
- if (res === null) {
1741
- throw new Error('Неверный код');
1742
- }
1743
- });
1744
- };
1745
- const saveToken = (data) => {
1746
- if (data?.access_token && data?.refresh_token) {
1747
- globalThis.sessionStorage.setItem('accessToken', data.access_token);
1748
- globalThis.sessionStorage.setItem('refreshToken', data.refresh_token);
1749
- }
1750
- };
1751
- const fetchMain = (body) => API$1.checkCode(body);
1752
- const transformBody = ({ smsText, smsCodesSetName, body, reqId }, isRetail) => {
1753
- if (isRetail) {
1754
- return { smsText, smsCodesSetName };
1755
- }
1756
- if (!reqId || !body) {
1757
- throw new Error('Произошла ошибка, попробуйте позднее');
1758
- }
1759
- return { code: smsText, reqId, body };
1760
- };
1761
-
1762
- const TIME_TO_RESEND = 180;
1763
- const useVerifyPhoneDialogSubmit = ({ values, onSuccess, formatData, reqId, isRetail = true, }) => {
1764
- const sessionStore = useSessionStore();
1765
- const attempts = sessionStore.smsCode?.attempts || 0;
1766
- const timer = Math.max(getTimer(sessionStore.smsCode?.sendTime || Date.now()), 0);
1767
- const [errorText, setErrorText] = useState('');
1768
- const [isLoading, { setTrue: startLoading, setFalse: endLoading }] = useBool(false);
1769
- const [timeNextReq, setTimeNextReq] = useState(timer);
1770
- const resetError = useCallback(() => setErrorText(''), []);
1771
- const isTimeExpired = Boolean(timeNextReq === 0 && sessionStore.smsCode?.sendTime);
1772
- const isSubmitButtonDisabled = attempts > 2 || isTimeExpired || !values.every(Boolean);
1773
- const handleSubmit = useCallback(async () => {
1774
- try {
1775
- sessionStore.smsCode = {
1776
- ...sessionStore.smsCode,
1777
- attempts: attempts + 1,
1778
- };
1779
- startLoading();
1780
- await checkCode({
1781
- smsText: values.join(''),
1782
- smsCodesSetName: { key: 'AUTHENTICATION' },
1783
- body: isRetail ? undefined : formatData,
1784
- reqId: isRetail ? undefined : reqId,
1785
- }, isRetail);
1786
- setTimeNextReq(0);
1787
- resetError();
1788
- sessionStore.smsCode = null;
1789
- await onSuccess?.(values.join(''));
1790
- }
1791
- catch {
1792
- setErrorText(attempts > 1 ? 'Исчерпан лимит ввода смс-кода' : 'Неверный код');
1793
- }
1794
- finally {
1795
- endLoading();
1796
- }
1797
- }, [values, attempts]);
1798
- useEffect(() => {
1799
- if (isTimeExpired && isRetail) {
1800
- setErrorText('Код просрочен');
1801
- }
1802
- else if (attempts > 2) {
1803
- setErrorText('Исчерпан лимит ввода смс-кода');
1804
- }
1805
- }, [isTimeExpired]);
1806
- return {
1807
- handleSubmit,
1808
- hasError: Boolean(errorText),
1809
- errorText,
1810
- isLoading,
1811
- timeNextReq,
1812
- isSubmitButtonDisabled,
1813
- setTimeNextReq,
1814
- setErrorText,
1815
- };
1816
- };
1817
- const getTimer = (sendTime) => TIME_TO_RESEND - Math.floor((Date.now() - sendTime) / 1000);
1247
+ return [formState, setFormState];
1248
+ }
1818
1249
 
1819
- const CODE_LENGTH = 4;
1820
- const VerifyPhoneDialog = JSX(
1821
- // eslint-disable-next-line max-lines-per-function
1822
- ({ phone, withDescription = true, consents, onSuccess = noop, onClose = noop, formatData, reqId, isRetail = true, }) => {
1823
- const [values, setValues] = useState(Array(CODE_LENGTH).fill(''));
1824
- const sessionStore = useSessionStore();
1825
- const [requestId, setRequestId] = useState(reqId);
1826
- const { handleSubmit, hasError, errorText, isLoading, timeNextReq, isSubmitButtonDisabled, setTimeNextReq, setErrorText, } = useVerifyPhoneDialogSubmit({
1827
- values,
1828
- onSuccess,
1829
- formatData,
1830
- reqId: requestId,
1831
- isRetail,
1832
- });
1833
- const captchaDialog = useDialog(CaptchaDialog);
1834
- const phoneNumber = formatPhone(phone);
1835
- const restartTimer = useCountDownTimer({ seconds: timeNextReq, onTick: setTimeNextReq });
1836
- const handleSendCode = useCallback(async () => {
1837
- const response = await sendCode({
1838
- phoneNumber,
1839
- smsCodesSetName: { key: 'AUTHENTICATION' },
1840
- }, isRetail);
1841
- if (response) {
1842
- setTimeNextReq(TIME_TO_RESEND);
1843
- restartTimer(TIME_TO_RESEND);
1844
- setErrorText('');
1845
- sessionStore.smsCode = {
1846
- sendTime: Date.now(),
1847
- attempts: 0,
1848
- };
1849
- if (!isRetail) {
1850
- setRequestId(String(response));
1851
- }
1250
+ function useForm(initialState, { resetOnSubmit, formValidator, normalizer, onChange, onSubmit } = {}) {
1251
+ const [formState, setFormState] = useNormalizedFormState(initialState, normalizer, onChange);
1252
+ const fieldRefs = useRef(getRefsObject(initialState));
1253
+ const [isDirtyForm, { setTrue: markAsDirty, setFalse: markAsClean }] = useBool(false);
1254
+ const dirtyFieldsMap = useRef({});
1255
+ const [fieldValidatorsMap, { isValid, errors }] = useFormValidator(formState, formValidator);
1256
+ const field = useCallback((fieldName, options = {}) => {
1257
+ const { parse, format, onChange: onFieldChange } = options;
1258
+ const value = formState[fieldName];
1259
+ const isDirty = isDirtyForm || dirtyFieldsMap.current?.[String(fieldName)];
1260
+ const fieldValidator = options?.validator ?? fieldValidatorsMap[fieldName];
1261
+ const fieldErrors = isDirty && fieldValidator ? fieldValidator(value) : [];
1262
+ return {
1263
+ setFieldRef: (_) => {
1264
+ if (fieldRefs.current) {
1265
+ fieldRefs.current[fieldName] = _;
1266
+ }
1267
+ },
1268
+ fieldRef: fieldRefs.current?.[fieldName],
1269
+ value: format ? format(value) : value,
1270
+ isDirty,
1271
+ errors: fieldValidator && fieldErrors,
1272
+ error: fieldErrors[0],
1273
+ onChange: (_) => {
1274
+ dirtyFieldsMap.current ||= {};
1275
+ dirtyFieldsMap.current[String(fieldName)] = true;
1276
+ const fieldVal = parse ? parse(_) : _;
1277
+ onFieldChange?.(fieldVal);
1278
+ setFormState((prev) => ({ ...prev, [fieldName]: fieldVal }));
1279
+ },
1280
+ };
1281
+ }, [formState, isDirtyForm, fieldValidatorsMap, setFormState]);
1282
+ const update = useCallback((_) => {
1283
+ dirtyFieldsMap.current = _;
1284
+ setFormState(_);
1285
+ }, [setFormState]);
1286
+ const reset = useCallback(() => {
1287
+ dirtyFieldsMap.current = {};
1288
+ markAsClean();
1289
+ setFormState(initialState);
1290
+ }, [initialState, setFormState]);
1291
+ const handleSubmit = useCallback((ev) => {
1292
+ ev.preventDefault();
1293
+ if (isValid) {
1294
+ resetOnSubmit && reset();
1295
+ onSubmit?.(formState, ev);
1852
1296
  }
1853
1297
  else {
1854
- captchaDialog.open({ phoneNumber, sendCode: handleSendCode });
1855
- }
1856
- }, [phoneNumber, restartTimer, onClose]);
1857
- useEffect(() => {
1858
- if (!sessionStore.smsCode?.sendTime && isRetail) {
1859
- handleSendCode();
1298
+ const errorFieldName = getErrorFieldName(formState, fieldValidatorsMap);
1299
+ markAsDirty();
1300
+ field(errorFieldName).fieldRef?.scrollIntoView({ behavior: 'smooth' });
1860
1301
  }
1861
- }, []);
1862
- return (jsx(Dialog, { maxWidth: "lg", onClose: onClose, children: jsxs("div", { className: "flex flex-col gap-xl items-center rounded-md", children: [renderHeadline(phone), jsx(InputCode, { values: values, setValues: setValues, errorText: errorText, hasError: hasError }), renderText$4(timeNextReq, handleSendCode), renderDescription$2(consents, withDescription), renderNextButton(isSubmitButtonDisabled, handleSubmit), isLoading ? jsx(Loader, { blur: false }) : null] }) }));
1863
- });
1864
- const renderHeadline = (phone) => (jsx(Headline, { className: "w-full", title: "\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u0435 \u043D\u043E\u043C\u0435\u0440 \u0442\u0435\u043B\u0435\u0444\u043E\u043D\u0430", description: `Мы отправили код на ${phone}`, headlineVersion: "XS", isEmbedded: true, as: "h6" }));
1865
- const renderDescription$2 = (consents, isRender = false) => isRender ? (jsxs(RichText, { itemSize: "list-s", children: [jsx("span", { children: "\u0412\u0432\u043E\u0434\u044F \u043A\u043E\u0434, \u044F \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u044E, \u0447\u0442\u043E \u043E\u0437\u043D\u0430\u043A\u043E\u043C\u043B\u0435\u043D \u0438 \u043F\u043E\u0434\u043F\u0438\u0441\u044B\u0432\u0430\u044E: " }), jsx("ul", { children: consents?.map((_, i) => (jsx("li", { children: _ }, `${_}-${i}`))) })] })) : null;
1866
- const renderNextButton = (disabled, onClick) => (jsx(SubmitButton, { text: "\u0414\u0430\u043B\u0435\u0435", disabled: disabled, onClick: onClick }));
1867
- const renderText$4 = (timeNextReq, handleSendCode) => timeNextReq ? (jsxs("div", { className: "flex flex-row text-l font-light text-base", children: ["\u041F\u043E\u043B\u0443\u0447\u0438\u0442\u044C \u043D\u043E\u0432\u044B\u0439 \u043A\u043E\u0434 \u043C\u043E\u0436\u043D\u043E \u0447\u0435\u0440\u0435\u0437", jsx(Timer, { className: "pl-2xs", seconds: timeNextReq })] })) : (jsx(Button, { embedded: true, onClick: handleSendCode, children: jsx(ButtonTitle, { children: "\u041F\u043E\u043B\u0443\u0447\u0438\u0442\u044C \u043D\u043E\u0432\u044B\u0439 \u043A\u043E\u0434" }) }));
1302
+ }, [resetOnSubmit, formState, isValid, reset, onSubmit]);
1303
+ return [formState, { errors, field, update, reset, onSubmit: handleSubmit }];
1304
+ }
1305
+ const getRefsObject = (initialState) => Object.keys(initialState).reduce((acc, key) => ({ ...acc, [key]: null }), {});
1306
+ const getErrorFieldName = (formState, fieldValidatorsMap = {}) => {
1307
+ const [errorFieldName = ''] = Object.entries(formState).find(([fieldName, value]) => {
1308
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
1309
+ return fieldValidatorsMap[fieldName]?.(value)?.length;
1310
+ }) ?? [];
1311
+ return errorFieldName;
1312
+ };
1868
1313
 
1869
1314
  const ApplicationFormLayout = JSX((props) => {
1870
1315
  const { className, title, children, ...rest } = props;
1871
1316
  return (jsx(BlockWrapper, { className: className, defaultPadding: "p-6xl", ...rest, children: jsxs("div", { className: "container space-y-lg", children: [title ? jsx(Heading, { headingType: "h3", title: title, className: "@xl:text-center" }) : null, children] }) }));
1872
1317
  });
1873
1318
 
1319
+ function useDialog(Dialog, initialProps = {}) {
1320
+ const { open, close, ...rest } = useDialogManager();
1321
+ const openDialog = useCallback((props, options = {}) => open({
1322
+ dialog: (jsx(Dialog, { ...initialProps, ...props, onClose: () => {
1323
+ close();
1324
+ props.onClose?.();
1325
+ } })),
1326
+ ...options,
1327
+ }), [Dialog, open, close]);
1328
+ return { open: openDialog, close, ...rest };
1329
+ }
1330
+
1874
1331
  const getConsentDataProcessing = (inputs) => inputs?.find((_) => _?.name === 'consentDataProcessing');
1875
1332
 
1876
1333
  // TODO Базовая функицональность всех Control - надо вынести и привязать к required флагу
@@ -1894,6 +1351,12 @@
1894
1351
  return debouncedCallback;
1895
1352
  };
1896
1353
 
1354
+ const noop = () => {
1355
+ // Do nothing
1356
+ };
1357
+
1358
+ const renderLabel$1 = (label) => label ? (jsx(Text, { size: "text-m", color: "text-primary-text", font: "font-light", children: label })) : null;
1359
+
1897
1360
  function useEventListener(target, type, listener, options) {
1898
1361
  useEffect(() => {
1899
1362
  if (!target || !listener) {
@@ -1917,6 +1380,30 @@
1917
1380
  return targetRef;
1918
1381
  }
1919
1382
 
1383
+ const inputValidStyle = 'border border-solid outline-none border-gray hover:border-primary-hover active:border-primary-text focus:border-primary-text rounded';
1384
+
1385
+ const getValidStyle = (valid) => (valid ? inputValidStyle : 'border-error');
1386
+
1387
+ const Input = JSX(
1388
+ // eslint-disable-next-line max-lines-per-function
1389
+ ({ key, className, id, name, type = 'text', label, placeholder, value = '', valid = true, pattern, autoFocus = false, isTextarea = false, disabled = false, children, onChange, onFocus, onBlur, }) => {
1390
+ const inputRef = useRef(null);
1391
+ const handleChange = useCallback((e) => {
1392
+ const valueWithoutSpace = (e.target?.value ?? '').trimStart();
1393
+ onChange && onChange(valueWithoutSpace);
1394
+ }, [onChange]);
1395
+ useEffect(() => {
1396
+ if (autoFocus) {
1397
+ inputRef.current?.focus();
1398
+ }
1399
+ }, [autoFocus, inputRef]);
1400
+ const paddingStyle = children ? 'pr-3xl' : '';
1401
+ const validStyle = getValidStyle(valid);
1402
+ const ariaLabel = label ?? name ?? id;
1403
+ return (jsxs("div", { className: style('relative', className), children: [jsxs("label", { className: "space-y-xs", children: [renderLabel$1(label), isTextarea ? (jsx("textarea", { className: style('block resize-y min-h-24', defaultStyle$1, validStyle), id: style('textarea', id), value: value, name: name || id, placeholder: placeholder, disabled: disabled, "aria-label": ariaLabel, onChange: handleChange, onFocus: onFocus, onBlur: onBlur }, key)) : (jsx("input", { ref: inputRef, className: style('h-14', defaultStyle$1, paddingStyle, validStyle), id: id, type: type, value: value, name: name || id, placeholder: placeholder, pattern: pattern, disabled: disabled, "aria-label": ariaLabel, onChange: handleChange, onFocus: onFocus, onBlur: onBlur }, key))] }), children] }));
1404
+ });
1405
+ const defaultStyle$1 = 'w-full border rounded-md text-primary-text outline-none p-m';
1406
+
1920
1407
  const InputWrapper = JSX(({ className, label, value = '', error, errors, type, isInteger, placeholder, maxLength, inputRef, isOpen, onOpen, onClose, onChange = noop, ...rest }) => {
1921
1408
  const popupRef = useOutsideClick(onClose);
1922
1409
  const handleChange = useCallback((v) => {
@@ -2711,9 +2198,9 @@
2711
2198
  onChange && onChange(!value);
2712
2199
  }, [onChange, disabled, value]);
2713
2200
  const icon = isRadio ? (jsx("div", { className: "absolute left-1 w-3 h-3 rounded-full bg-primary-main" })) : (jsx(SVG, { paths: CHECK_PATHS, className: "absolute left-1 ml-px block", width: "11", height: "9", fill: "white", viewBox: "0 0 11 9" }));
2714
- return (jsx("div", { className: className, children: jsxs("label", { className: style('flex items-center relative group/box', getCursorStyle(disabled)), onClick: handleChange, children: [jsx("div", { className: style(defaultCheckStyle, 'm-0', isRadio ? 'rounded-full border-2' : checkboxStyle(value), !disabled && value ? 'border-primary-main' : 'border-gray', disabled ? 'bg-main-disabled' : 'group-hover/box:border-primary-hover'), role: role(isRadio), "aria-checked": Boolean(value), "aria-disabled": Boolean(disabled), "aria-label": text }), value ? icon : null, renderText$3(text)] }) }));
2201
+ return (jsx("div", { className: className, children: jsxs("label", { className: style('flex items-center relative group/box', getCursorStyle(disabled)), onClick: handleChange, children: [jsx("div", { className: style(defaultCheckStyle, 'm-0', isRadio ? 'rounded-full border-2' : checkboxStyle(value), !disabled && value ? 'border-primary-main' : 'border-gray', disabled ? 'bg-main-disabled' : 'group-hover/box:border-primary-hover'), role: role(isRadio), "aria-checked": Boolean(value), "aria-disabled": Boolean(disabled), "aria-label": text }), value ? icon : null, renderText$4(text)] }) }));
2715
2202
  });
2716
- const renderText$3 = (text) => text ? (jsx("div", { className: "ml-s", children: jsx(Text, { size: "text-l", font: "font-light", children: text }) })) : null;
2203
+ const renderText$4 = (text) => text ? (jsx("div", { className: "ml-s", children: jsx(Text, { size: "text-l", font: "font-light", children: text }) })) : null;
2717
2204
  const getCursorStyle = (disabled = false) => (disabled ? 'cursor-not-allowed' : 'cursor-pointer');
2718
2205
  const role = (isRadio = false) => (isRadio ? 'radio' : 'checkbox');
2719
2206
  const checkboxStyle = (value = false) => style('rounded border', { 'bg-primary-main': value });
@@ -3062,6 +2549,39 @@
3062
2549
 
3063
2550
  const renderTitle = (title) => title ? (jsx("div", { className: "@xl:text-center @xl:col-span-2 mb-m", children: jsx(Text, { size: "text-h6", children: title }) })) : null;
3064
2551
 
2552
+ const themeStyle$1 = {
2553
+ primary: style('text-white bg-primary-main hover:bg-primary-hover active:bg-primary-active', 'group-data-secondary:text-primary-main group-data-secondary:bg-white', 'group-data-secondary:hover:text-white group-data-secondary:hover:bg-primary-hover', 'group-data-secondary:active:bg-primary-active'),
2554
+ secondary: style('text-primary-main bg-main-divider hover:text-white hover:bg-primary-hover active:bg-primary-active', 'group-data-secondary:text-white group-data-secondary:bg-white/20', 'group-data-secondary:hover:bg-primary-hover', 'group-data-secondary:active:bg-primary-active'),
2555
+ };
2556
+ const embeddedStyle = style('group/btn-embedded', 'bg-transparent border border-transparent outline-none');
2557
+ const disabledStyle = style('bg-main-gray text-main-disabled cursor-not-allowed');
2558
+ const Button = JSX(({ className, type = 'button', version = 'primary', shape = 'default', embedded, disabled, role, ariaLabel, data, dataTheme, children, wcmsIgnore, onClick = noop, }) => {
2559
+ const handleClick = useCallback(role !== 'tab' ? handlerDecorator(onClick) : onClick, [
2560
+ role,
2561
+ onClick,
2562
+ ]);
2563
+ const aspectsAttrs = useMemo(() => getAspectsAttributes(data), [data]);
2564
+ const isRound = shape === 'round';
2565
+ return (jsx("button", { className: style('font-sans flex items-center gap-xs', {
2566
+ [themeStyle$1[version]]: !disabled && !embedded,
2567
+ [embeddedStyle]: embedded,
2568
+ [disabledStyle]: disabled,
2569
+ }, embedded ? 'justify-between' : 'justify-center', embedded || isRound ? 'p-0' : 'px-9 py-4', {
2570
+ 'rounded-md': shape === 'default',
2571
+ 'rounded-full': isRound,
2572
+ }, className), type: type, role: role, "aria-label": ariaLabel, disabled: disabled, "aria-disabled": disabled ? 'true' : undefined, "data-theme": dataTheme, "data-wcms-ignore": wcmsIgnore, ...aspectsAttrs, onClick: handleClick, children: children }));
2573
+ });
2574
+
2575
+ const CloseButton = JSX(({ className, onClose }) => (jsx("button", { className: style('flex justify-center items-center w-12 h-12 p-2xs bg-transparent border-none', className), onClick: onClose, title: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C", type: "button", children: jsx(Icon, { name: "CloseIcon", width: "20", height: "20", iconVersion: "gray" }) })));
2576
+
2577
+ const DIALOG_STYLE = {
2578
+ sm: 'max-w-sm top-1/3',
2579
+ lg: 'max-w-lg',
2580
+ '4xl': 'max-w-4xl',
2581
+ none: 'mt-0',
2582
+ };
2583
+ const Dialog = JSX(({ head, maxWidth = '4xl', children, onClose, onClick }) => (jsxs("div", { className: style('relative bg-white p-lg pb-6xl my-6xl mx-auto rounded-lg w-full', DIALOG_STYLE[maxWidth]), role: "dialog", title: "\u0414\u0438\u0430\u043B\u043E\u0433", onClick: onClick, children: [jsxs("div", { className: "sticky py-xl top-0 bg-white z-10", children: [jsx(CloseButton, { className: "absolute top-0 right-0 z-10", onClose: onClose }), jsx("div", { className: "container", children: head })] }), jsx("div", { className: "container", children: children })] })));
2584
+
3065
2585
  const ResponseTypeDialog = JSX(({ ok, typeForm, onClose }) => {
3066
2586
  const statusIcon = ok ? 'ResponseOKIcon' : 'ResponseFailIcon';
3067
2587
  const responseOKDescription = typeForm === 'ANTIFRAUD'
@@ -3146,34 +2666,203 @@
3146
2666
  return { ...formState, typeForm: { key: typeForm, text: '' } };
3147
2667
  };
3148
2668
 
3149
- const themeStyle = {
3150
- primary: themeStyle$1.primary,
3151
- secondary: themeStyle$1.secondary,
3152
- white: 'text-primary-main bg-white hover:text-white hover:bg-primary-hover active:bg-white active:text-primary-main',
3153
- link: 'text-primary-main',
3154
- gray: themeStyle$1.secondary,
3155
- transparent: '',
3156
- '': '',
2669
+ const Loader = JSX(({ color = 'text-primary-main', position = 'absolute', blur = true, size = 'big' }) => (jsx("div", { className: style('flex justify-center items-center h-full w-full z-50', position, {
2670
+ 'backdrop-blur': blur,
2671
+ }), children: jsx("div", { className: style('inline-block', 'animate-spin rounded-full', 'border-solid border-current', 'border-r-transparent', size === 'extraSmall' && 'border-2 h-4 w-4', size === 'small' && 'border-4 h-8 w-8', size === 'big' && 'border-8 h-28 w-28', color), role: "status" }) })));
2672
+
2673
+ const SubmitButton$1 = JSX(({ isLoading, disabled, children, className, ...rest }) => (jsxs(Button, { type: "submit", className: style('relative', className), disabled: isLoading || disabled, ...rest, children: [isLoading ? jsx(Loader, { blur: true, size: "small" }) : null, children] })));
2674
+
2675
+ const themeStyle = {
2676
+ primary: themeStyle$1.primary,
2677
+ secondary: themeStyle$1.secondary,
2678
+ white: 'text-primary-main bg-white hover:text-white hover:bg-primary-hover active:bg-white active:text-primary-main',
2679
+ link: 'text-primary-main',
2680
+ gray: themeStyle$1.secondary,
2681
+ transparent: '',
2682
+ '': '',
2683
+ };
2684
+ const Link = JSX((props) => {
2685
+ const link = useLink();
2686
+ const { className, href, target, text, aboveText, version = 'link', rel, ariaLabel, data, children, onClick, } = link(props);
2687
+ const buttonLike = version !== 'link';
2688
+ return (jsx("a", { className: style('group/btn inline-flex items-center h-fit', 'font-sans no-underline select-none', 'border border-transparent focus:border-primary-text focus:border', 'cursor-pointer', {
2689
+ [themeStyle[version]]: Boolean(version),
2690
+ [aboveText ? 'px-9 py-2.5' : 'px-9 py-4']: buttonLike,
2691
+ 'rounded-md': buttonLike,
2692
+ }, className), href: href, target: target, rel: rel, "aria-label": ariaLabel ?? `Ссылка на ${text}`, role: href ? 'link' : 'button', onClick: onClick, ...getAspectsAttributes(data), children: children ?? renderText$3(text, aboveText) }));
2693
+ });
2694
+ const renderText$3 = (text, aboveText) => text || aboveText ? (jsxs("div", { className: "whitespace-pre", children: [aboveText ? jsx("div", { className: "font-light text-left text-xs", children: aboveText }) : null, jsx("div", { className: style('text-left', { 'text-s -mt-3xs': Boolean(aboveText) }), children: text })] })) : null;
2695
+
2696
+ const Footnote = JSX(({ text, link }) => (jsxs(Paragraph, { size: "text-l", font: "font-light", color: "text-secondary-text", children: [text ? jsx(Text, { children: text }) : null, link ? (jsx(Link, { ...link, ariaLabel: "\u0443\u0441\u043B\u043E\u0432\u0438\u044F \u043F\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0434\u0430\u043D\u043D\u044B\u0445", children: link.text })) : null] })));
2697
+
2698
+ const agreementText = 'Нажимая кнопку, вы подтверждаете согласие с ';
2699
+ const agreementTextPF = 'Нажимая на кнопку, вы подтверждаете, что клиент дал согласие на ';
2700
+ const renderAgreementSubmit = ({ consentDataProcessing, link, button, typeForm, }) => (jsxs("div", { className: "flex col-span-2 gap-xs flex-col w-full items-baseline", children: [consentDataProcessing ? (jsxs("div", { children: [jsxs("div", { className: "flex gap-3 items-center", children: [jsx(Checkbox, { ...consentDataProcessing }), jsx(Footnote, { link: link })] }), renderErrorText(withValidator(consentDataProcessing, agreementValidator).error)] })) : (jsx(Footnote, { text: typeForm === 'PF' ? agreementTextPF : agreementText, link: link })), jsx(SubmitButton$1, { className: "w-full @xl:w-auto", children: button?.text ? button.text : 'Отправить заявку' })] }));
2701
+
2702
+ const renderContacts = () => (jsxs("div", { className: "space-y-m", children: [jsx(Heading, { headingType: "h6", title: "\u0418\u043B\u0438 \u0441\u0432\u044F\u0436\u0438\u0442\u0435\u0441\u044C \u0441 \u043D\u0430\u043C\u0438 \u0443\u0434\u043E\u0431\u043D\u044B\u043C \u0441\u043F\u043E\u0441\u043E\u0431\u043E\u043C", className: "@xl:text-center" }), jsxs("div", { className: "flex sm:justify-center gap-xl", children: [jsxs("a", { className: "flex gap-s items-center text-primary-text no-underline", href: `tel:8 (800) 200-78-70`, "aria-label": "\u0442\u0435\u043B\u0435\u0444\u043E\u043D 8 (800) 200-78-70", children: [jsx(Img, { image: { icon: 'PhoneIcon' }, width: "24", height: "24" }), jsx("span", { children: "8 (800) 200-78-70" })] }), jsxs("a", { className: "flex gap-s items-center text-primary-text no-underline", "aria-label": "\u043F\u043E\u0447\u0442\u0430 ved@rshb.ru", href: `mailto:ved@rshb.ru`, children: [jsx(Img, { image: { icon: 'MailIcon' }, width: "24", height: "24" }), jsx("span", { children: "ved@rshb.ru" })] })] })] }));
2703
+
2704
+ const useInterval = (handler, period) => {
2705
+ const timer = useRef(null);
2706
+ const stop = useCallback(() => clearInterval(timer.current), []);
2707
+ const start = useCallback(() => {
2708
+ stop();
2709
+ timer.current = setInterval(() => handler(stop), period);
2710
+ }, [handler, period, stop]);
2711
+ useEffect(() => {
2712
+ start();
2713
+ return stop;
2714
+ }, [start, stop]);
2715
+ return { start, stop };
2716
+ };
2717
+
2718
+ function useCountDownTimer({ seconds, period = 1000, onTick, onEnd }) {
2719
+ const counter = useRef(seconds);
2720
+ const handleTick = useCallback((stop) => {
2721
+ counter.current ||= 0;
2722
+ counter.current = Math.max(0, counter.current - 1);
2723
+ try {
2724
+ onTick?.(counter.current);
2725
+ }
2726
+ finally {
2727
+ if (counter.current <= 0) {
2728
+ stop();
2729
+ onEnd?.();
2730
+ }
2731
+ }
2732
+ }, [onTick, onEnd]);
2733
+ const { start } = useInterval(handleTick, period);
2734
+ return useCallback((_) => {
2735
+ counter.current = _;
2736
+ start();
2737
+ }, []);
2738
+ }
2739
+
2740
+ const InputCode = JSX(({ values, setValues, hasError, errorText }) => {
2741
+ const [activeIndex, setActiveIndex] = useState(0);
2742
+ const inputRefs = useRef([]);
2743
+ useEffect(() => {
2744
+ inputRefs.current?.[activeIndex]?.focus();
2745
+ }, [activeIndex]);
2746
+ const handleChange = useCallback((index) => (event) => {
2747
+ const { value } = event.currentTarget;
2748
+ const oneValue = value.slice(0, 1);
2749
+ setValues(values.map((_, i) => (i === index ? oneValue : _)));
2750
+ setActiveIndex(index + 1);
2751
+ }, [values]);
2752
+ const handleKeyDown = useCallback((currentIndex) => (event) => {
2753
+ const { key } = event;
2754
+ if (key === 'Backspace' && !values[currentIndex]) {
2755
+ const previousIndex = currentIndex > 0 ? currentIndex - 1 : values.length - 1;
2756
+ const updatedValues = values.map((value, index) => (index === previousIndex ? '' : value));
2757
+ setValues(updatedValues);
2758
+ setActiveIndex(previousIndex);
2759
+ }
2760
+ }, [values]);
2761
+ const handlePaste = useCallback((event) => {
2762
+ event.preventDefault();
2763
+ const pastedData = event.clipboardData.getData('text');
2764
+ const updatedValues = values.map((_, idx) => (idx < pastedData.length ? pastedData[idx] : _));
2765
+ setValues(updatedValues);
2766
+ setActiveIndex(updatedValues.length - 1);
2767
+ }, [values]);
2768
+ return (jsxs("div", { className: "flex flex-col gap-2 text-center", children: [jsx("div", { children: values.map((value, index) => (jsx("input", { type: "number", maxLength: 1, value: value, onChange: handleChange(index), onPaste: handlePaste, ref: (ref) => {
2769
+ if (!inputRefs.current) {
2770
+ inputRefs.current = [];
2771
+ }
2772
+ inputRefs.current[index] = ref;
2773
+ }, onFocus: (event) => event.target.select(), onKeyDown: handleKeyDown(index), className: getInputStyle(index, values, hasError) }, index))) }), hasError ? jsx("div", { className: "text-error", children: errorText }) : null] }));
2774
+ });
2775
+ const getInputStyle = (index, values, hasError = false) => {
2776
+ const isInputEmpty = !values[index];
2777
+ return `w-16 sm:w-20 h-24 text-5xl text-center p-md m-2 border ${getValidStyle(!hasError || !isInputEmpty)} rounded-md caret-transparent outline-none`;
2778
+ };
2779
+
2780
+ const SubmitButton = JSX(({ disabled = false, onClick, text }) => (jsx(Button, { type: "button", onClick: onClick, disabled: disabled, children: jsx(Text, { font: "font-normal", children: text }) })));
2781
+
2782
+ const ButtonTitle = JSX(({ className, children }) => (jsx("span", { className: style('inline-flex items-center text-start gap-s group-[]/btn-embedded:text-primary-main', className), children: children })));
2783
+
2784
+ const Timer = JSX(({ className, seconds }) => (jsx("span", { className: className, children: formatTimer(seconds) })));
2785
+ const formatTimer = (seconds) => {
2786
+ const minutes = Math.floor(seconds / 60);
2787
+ return [minutes, seconds % 60].map((_) => String(_).padStart(2, '0')).join(':');
3157
2788
  };
3158
- const Link = JSX((props) => {
3159
- const link = useLink();
3160
- const { className, href, target, text, aboveText, version = 'link', rel, ariaLabel, data, children, onClick, } = link(props);
3161
- const buttonLike = version !== 'link';
3162
- return (jsx("a", { className: style('group/btn inline-flex items-center h-fit', 'font-sans no-underline select-none', 'border border-transparent focus:border-primary-text focus:border', 'cursor-pointer', {
3163
- [themeStyle[version]]: Boolean(version),
3164
- [aboveText ? 'px-9 py-2.5' : 'px-9 py-4']: buttonLike,
3165
- 'rounded-md': buttonLike,
3166
- }, className), href: href, target: target, rel: rel, "aria-label": ariaLabel ?? `Ссылка на ${text}`, role: href ? 'link' : 'button', onClick: onClick, ...getAspectsAttributes(data), children: children ?? renderText$2(text, aboveText) }));
3167
- });
3168
- const renderText$2 = (text, aboveText) => text || aboveText ? (jsxs("div", { className: "whitespace-pre", children: [aboveText ? jsx("div", { className: "font-light text-left text-xs", children: aboveText }) : null, jsx("div", { className: style('text-left', { 'text-s -mt-3xs': Boolean(aboveText) }), children: text })] })) : null;
3169
2789
 
3170
- const Footnote = JSX(({ text, link }) => (jsxs(Paragraph, { size: "text-l", font: "font-light", color: "text-secondary-text", children: [text ? jsx(Text, { children: text }) : null, link ? (jsx(Link, { ...link, ariaLabel: "\u0443\u0441\u043B\u043E\u0432\u0438\u044F \u043F\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0434\u0430\u043D\u043D\u044B\u0445", children: link.text })) : null] })));
2790
+ const VerifyPhoneDialogLayout = JSX(({ children, isLoading, isSubmitButtonDisabled, timeNextReq, onSubmit, onSendCode, values, onChange, errorText, hasError, phone, onClose, }) => (jsx(Dialog, { maxWidth: "lg", onClose: onClose, children: jsxs("div", { className: "flex flex-col gap-xl items-center rounded-md", children: [renderHeadline(phone), jsx(InputCode, { values: values, setValues: onChange, errorText: errorText, hasError: hasError }), renderText$2(timeNextReq, onSendCode), children, renderNextButton(isSubmitButtonDisabled, onSubmit), isLoading ? jsx(Loader, { blur: false }) : null] }) })));
2791
+ const renderHeadline = (phone) => (jsx(Headline, { className: "w-full", title: "\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u0435 \u043D\u043E\u043C\u0435\u0440 \u0442\u0435\u043B\u0435\u0444\u043E\u043D\u0430", description: `Мы отправили код на ${phone}`, headlineVersion: "XS", isEmbedded: true, as: "h6" }));
2792
+ const renderNextButton = (disabled, onClick) => (jsx(SubmitButton, { text: "\u0414\u0430\u043B\u0435\u0435", disabled: disabled, onClick: onClick }));
2793
+ const renderText$2 = (timeNextReq, handleSendCode) => timeNextReq ? (jsxs("div", { className: "flex flex-row text-l font-light text-base gap-2xs", children: [jsx("span", { children: " \u041F\u043E\u043B\u0443\u0447\u0438\u0442\u044C \u043D\u043E\u0432\u044B\u0439 \u043A\u043E\u0434 \u043C\u043E\u0436\u043D\u043E \u0447\u0435\u0440\u0435\u0437" }), jsx(Timer, { seconds: timeNextReq })] })) : (jsx(Button, { embedded: true, onClick: handleSendCode, children: jsx(ButtonTitle, { children: "\u041F\u043E\u043B\u0443\u0447\u0438\u0442\u044C \u043D\u043E\u0432\u044B\u0439 \u043A\u043E\u0434" }) }));
3171
2794
 
3172
- const agreementText = 'Нажимая кнопку, вы подтверждаете согласие с ';
3173
- const agreementTextPF = 'Нажимая на кнопку, вы подтверждаете, что клиент дал согласие на ';
3174
- const renderAgreementSubmit = ({ consentDataProcessing, link, button, typeForm, }) => (jsxs("div", { className: "flex col-span-2 gap-xs flex-col w-full items-baseline", children: [consentDataProcessing ? (jsxs("div", { children: [jsxs("div", { className: "flex gap-3 items-center", children: [jsx(Checkbox, { ...consentDataProcessing }), jsx(Footnote, { link: link })] }), renderErrorText(withValidator(consentDataProcessing, agreementValidator).error)] })) : (jsx(Footnote, { text: typeForm === 'PF' ? agreementTextPF : agreementText, link: link })), jsx(SubmitButton$1, { className: "w-full @xl:w-auto", children: button?.text ? button.text : 'Отправить заявку' })] }));
2795
+ const TIME_TO_RESEND$2 = 180;
2796
+ const getTimer = (sendTime) => TIME_TO_RESEND$2 - Math.floor((Date.now() - sendTime) / 1000);
3175
2797
 
3176
- const renderContacts = () => (jsxs("div", { className: "space-y-m", children: [jsx(Heading, { headingType: "h6", title: "\u0418\u043B\u0438 \u0441\u0432\u044F\u0436\u0438\u0442\u0435\u0441\u044C \u0441 \u043D\u0430\u043C\u0438 \u0443\u0434\u043E\u0431\u043D\u044B\u043C \u0441\u043F\u043E\u0441\u043E\u0431\u043E\u043C", className: "@xl:text-center" }), jsxs("div", { className: "flex sm:justify-center gap-xl", children: [jsxs("a", { className: "flex gap-s items-center text-primary-text no-underline", href: `tel:8 (800) 200-78-70`, "aria-label": "\u0442\u0435\u043B\u0435\u0444\u043E\u043D 8 (800) 200-78-70", children: [jsx(Img, { image: { icon: 'PhoneIcon' }, width: "24", height: "24" }), jsx("span", { children: "8 (800) 200-78-70" })] }), jsxs("a", { className: "flex gap-s items-center text-primary-text no-underline", "aria-label": "\u043F\u043E\u0447\u0442\u0430 ved@rshb.ru", href: `mailto:ved@rshb.ru`, children: [jsx(Img, { image: { icon: 'MailIcon' }, width: "24", height: "24" }), jsx("span", { children: "ved@rshb.ru" })] })] })] }));
2798
+ const API$2 = LeadServiceAPI();
2799
+ const useVerifyPhoneDialogSubmit$1 = ({ values, onSuccess, formatData, reqId, }) => {
2800
+ const timer = Math.max(getTimer(Date.now()), 0);
2801
+ const [errorText, setErrorText] = useState('');
2802
+ const [isLoading, { setTrue: startLoading, setFalse: endLoading }] = useBool(false);
2803
+ const [timeNextReq, setTimeNextReq] = useState(timer);
2804
+ const resetError = useCallback(() => setErrorText(''), []);
2805
+ const isSubmitButtonDisabled = !values.every(Boolean);
2806
+ const handleSubmit = useCallback(async () => {
2807
+ try {
2808
+ startLoading();
2809
+ const response = await API$2.checkCode({
2810
+ code: values.join(''),
2811
+ body: formatData,
2812
+ reqId,
2813
+ });
2814
+ if (response?.errorDesc) {
2815
+ setErrorText(response?.errorDesc);
2816
+ return;
2817
+ }
2818
+ setTimeNextReq(0);
2819
+ resetError();
2820
+ onSuccess?.(values.join(''));
2821
+ }
2822
+ catch {
2823
+ setErrorText('Неверный код');
2824
+ }
2825
+ finally {
2826
+ endLoading();
2827
+ }
2828
+ }, [values]);
2829
+ return {
2830
+ handleSubmit,
2831
+ hasError: Boolean(errorText),
2832
+ errorText,
2833
+ isLoading,
2834
+ timeNextReq,
2835
+ isSubmitButtonDisabled,
2836
+ setTimeNextReq,
2837
+ setErrorText,
2838
+ };
2839
+ };
2840
+
2841
+ const API$1 = LeadServiceAPI();
2842
+ const CODE_LENGTH$1 = 4;
2843
+ const TIME_TO_RESEND$1 = 180;
2844
+ const VerifyPhoneDialog$1 = JSX(({ phone, onSuccess = noop, onClose = noop, formatData = {}, reqId }) => {
2845
+ const [values, setValues] = useState(Array(CODE_LENGTH$1).fill(''));
2846
+ const [requestId, setRequestId] = useState(reqId);
2847
+ const { handleSubmit, hasError, errorText, isLoading, timeNextReq, isSubmitButtonDisabled, setTimeNextReq, setErrorText, } = useVerifyPhoneDialogSubmit$1({
2848
+ values,
2849
+ onSuccess,
2850
+ formatData,
2851
+ reqId: requestId ?? '',
2852
+ });
2853
+ const phoneNumber = formatPhone(phone);
2854
+ const restartTimer = useCountDownTimer({ seconds: timeNextReq, onTick: setTimeNextReq });
2855
+ const handleSendCode = useCallback(async () => {
2856
+ const response = await API$1.sendCode({ phone: phoneNumber });
2857
+ if (response) {
2858
+ setTimeNextReq(TIME_TO_RESEND$1);
2859
+ restartTimer(TIME_TO_RESEND$1);
2860
+ setErrorText('');
2861
+ setRequestId(String(response));
2862
+ }
2863
+ }, [phoneNumber, restartTimer, onClose]);
2864
+ return (jsx(VerifyPhoneDialogLayout, { isSubmitButtonDisabled: isSubmitButtonDisabled, onSubmit: handleSubmit, onSendCode: handleSendCode, timeNextReq: timeNextReq, values: values, onChange: setValues, phone: phone, isLoading: isLoading, errorText: errorText, hasError: hasError, onClose: onClose }));
2865
+ });
3177
2866
 
3178
2867
  const API = LeadServiceAPI();
3179
2868
  const ApplicationForm = UniBlock(
@@ -3185,7 +2874,7 @@
3185
2874
  const aspects = useAspects();
3186
2875
  const formValidator = useMemo(() => getFormValidator(inputs), [inputs]);
3187
2876
  const responseTypeDialog = useDialog(ResponseTypeDialog);
3188
- const verifyPhoneDialog = useDialog(VerifyPhoneDialog);
2877
+ const verifyPhoneDialog = useDialog(VerifyPhoneDialog$1);
3189
2878
  const handleSubmit = useCallback(async (formData, ev) => {
3190
2879
  const formatData = getFormatData({ ...formData, ...additionalParams });
3191
2880
  if (endpoint === 'initcorporatelead') {
@@ -3197,10 +2886,8 @@
3197
2886
  }
3198
2887
  verifyPhoneDialog.open({
3199
2888
  phone,
3200
- withDescription: false,
3201
2889
  formatData,
3202
2890
  reqId: String(response),
3203
- isRetail: false,
3204
2891
  onSuccess: () => {
3205
2892
  verifyPhoneDialog.close();
3206
2893
  responseTypeDialog.open({ ok: true, typeForm });
@@ -3505,6 +3192,42 @@
3505
3192
  EsiaStatuses["Pending"] = "PENDING";
3506
3193
  })(EsiaStatuses || (EsiaStatuses = {}));
3507
3194
 
3195
+ const getTraceId = () => {
3196
+ const result = new Uint8Array(8);
3197
+ globalThis.crypto.getRandomValues(result);
3198
+ return result.reduce((acc, _) => `${acc}${_.toString(16).padStart(2, '0')}`, '');
3199
+ };
3200
+
3201
+ const fetchRetailJSON = async (url, method, body) => {
3202
+ try {
3203
+ const response = await doRequest(url, method, body);
3204
+ return response.json();
3205
+ }
3206
+ catch (err) {
3207
+ console.error(err);
3208
+ return null;
3209
+ }
3210
+ };
3211
+ async function doRequest(url, method, body) {
3212
+ const traceId = getTraceId();
3213
+ return globalThis?.fetch?.(`${RETAIL_API_BASE_URI}${url}`, {
3214
+ method,
3215
+ headers: {
3216
+ 'Content-Type': 'application/json',
3217
+ 'X-B3-Sampled': '1',
3218
+ 'X-B3-Spanid': traceId,
3219
+ 'X-B3-Traceid': traceId,
3220
+ ...getAuthorizationHeaders(),
3221
+ },
3222
+ credentials: 'include',
3223
+ body: body ? JSON.stringify(body) : null,
3224
+ });
3225
+ }
3226
+ const getAuthorizationHeaders = () => {
3227
+ const token = sessionStorage.getItem('accessToken');
3228
+ return token ? { Authorization: `Bearer ${token}` } : null;
3229
+ };
3230
+
3508
3231
  const getLink = (body) => fetchRetailJSON('/esia/getLink', 'POST', body);
3509
3232
 
3510
3233
  const EsiaLoginBanner = JSX(({ onChangeEsiaStatus, productType }) => {
@@ -4443,42 +4166,180 @@
4443
4166
 
4444
4167
  const API_BASE_URI = '/light-api-cash/v1';
4445
4168
 
4446
- const useLeadFormData = (typeField) => {
4447
- const { data, error } = useAsyncData(`${API_BASE_URI}/dictionary?dictionaryType=${encodeURIComponent(typeField)}`, fetchData);
4448
- if (data && 'errorMessage' in data) {
4449
- return { error };
4450
- }
4451
- return { data: data, error };
4452
- };
4453
- const fetchData = async (url) => {
4454
- const result = await fetchJSON(url, { method: 'POST' });
4455
- return result || [];
4456
- };
4169
+ const useLeadFormData = (typeField) => {
4170
+ const { data, error } = useAsyncData(`${API_BASE_URI}/dictionary?dictionaryType=${encodeURIComponent(typeField)}`, fetchData);
4171
+ if (data && 'errorMessage' in data) {
4172
+ return { error };
4173
+ }
4174
+ return { data: data, error };
4175
+ };
4176
+ const fetchData = async (url) => {
4177
+ const result = await fetchJSON(url, { method: 'POST' });
4178
+ return result || [];
4179
+ };
4180
+
4181
+ const AddressRetailField = JSX(({ field, input }) => {
4182
+ const [offices, setOffices] = useState([]);
4183
+ const { data } = useLeadFormData('REGION_RF');
4184
+ const regionValue = field('regionRetail')?.value || {};
4185
+ const addressField = field(input?.name ?? '');
4186
+ useEffect(() => {
4187
+ (async () => {
4188
+ const officesList = await fetchRegionOffices(regionValue?.key ?? '');
4189
+ setOffices(officesList);
4190
+ })();
4191
+ if (regionValue?.key) {
4192
+ addressField.onChange?.('');
4193
+ }
4194
+ }, [regionValue.key]);
4195
+ const { points } = useOfficesAtmsMapData({
4196
+ data: offices,
4197
+ filtrationState: {},
4198
+ getBalloon: getOfficePoint,
4199
+ });
4200
+ return (jsxs("div", { children: [jsx(SelectControl, { label: "\u0410\u0434\u0440\u0435\u0441 \u043E\u0442\u0434\u0435\u043B\u0435\u043D\u0438\u044F", placeholder: "\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u043E\u0442\u0434\u0435\u043B\u0435\u043D\u0438\u0435", options: offices.map(({ id, address = '' }) => ({
4201
+ key: id?.toString() || '',
4202
+ text: address,
4203
+ })), ...addressField, isSearch: true }), jsx("div", { className: "h-[600px]", children: jsx(ClientOnly, { children: jsx(YandexMap, { points: points, isLoad: !data, className: "h-full", selectedAddress: addressField?.value?.text }) }) })] }));
4204
+ });
4205
+
4206
+ function copy(source, target) {
4207
+ for (const [k, v] of source.entries()) {
4208
+ if (v !== null && v !== undefined) {
4209
+ target.setItem(k, v);
4210
+ }
4211
+ else {
4212
+ target.removeItem(k);
4213
+ }
4214
+ }
4215
+ }
4216
+
4217
+ function replicate(primary, secondary) {
4218
+ copy(primary, secondary);
4219
+ copy(secondary, primary);
4220
+ return primary.bus.watch(({ type, event }) => {
4221
+ if (event !== null && event !== undefined) {
4222
+ secondary.setItem(type, event);
4223
+ }
4224
+ else {
4225
+ secondary.removeItem(type);
4226
+ }
4227
+ });
4228
+ }
4229
+
4230
+ class StorageAdapter {
4231
+ storage;
4232
+ bus;
4233
+ get size() {
4234
+ return this.storage?.length ?? 0;
4235
+ }
4236
+ constructor(storage, bus = new EventBus()) {
4237
+ this.storage = storage;
4238
+ this.bus = bus;
4239
+ }
4240
+ hasItem(key) {
4241
+ return Boolean(this.storage?.getItem(String(key)));
4242
+ }
4243
+ getItem(key) {
4244
+ const _ = this.storage?.getItem(String(key)) ?? null;
4245
+ try {
4246
+ return JSON.parse(String(_));
4247
+ }
4248
+ catch (ex) {
4249
+ return null;
4250
+ }
4251
+ }
4252
+ entries() {
4253
+ return Array.from({ length: this.size }, (_, i) => {
4254
+ const k = String(this.storage?.key(i));
4255
+ return [k, this.getItem(k)];
4256
+ });
4257
+ }
4258
+ setItem(key, value) {
4259
+ if (value !== null) {
4260
+ this.storage?.setItem(String(key), JSON.stringify(value));
4261
+ }
4262
+ else {
4263
+ this.storage?.removeItem(String(key));
4264
+ }
4265
+ this.bus?.subject(key, value);
4266
+ }
4267
+ removeItem(key) {
4268
+ this.storage?.removeItem(String(key));
4269
+ this.bus?.subject(key, null);
4270
+ }
4271
+ }
4272
+
4273
+ class Store {
4274
+ bus;
4275
+ store = new Map();
4276
+ get size() {
4277
+ return this.store.size;
4278
+ }
4279
+ constructor(bus = new EventBus()) {
4280
+ this.bus = bus;
4281
+ }
4282
+ hasItem(key) {
4283
+ return this.store.has(key);
4284
+ }
4285
+ getItem(key) {
4286
+ return this.store.get(key);
4287
+ }
4288
+ entries() {
4289
+ return this.store.entries();
4290
+ }
4291
+ setItem(key, value) {
4292
+ this.store.set(key, value);
4293
+ this.bus.subject(key, value);
4294
+ }
4295
+ removeItem(key) {
4296
+ this.store.delete(key);
4297
+ this.bus.subject(key, null);
4298
+ }
4299
+ }
4300
+
4301
+ function useRerender() {
4302
+ const [, setCount] = useState(0);
4303
+ return useCallback(() => setCount(_ => (_ + 1) % (1 << 16)), []);
4304
+ }
4457
4305
 
4458
- const AddressRetailField = JSX(({ field, input }) => {
4459
- const [offices, setOffices] = useState([]);
4460
- const { data } = useLeadFormData('REGION_RF');
4461
- const regionValue = field('regionRetail')?.value || {};
4462
- const addressField = field(input?.name ?? '');
4463
- useEffect(() => {
4464
- (async () => {
4465
- const officesList = await fetchRegionOffices(regionValue?.key ?? '');
4466
- setOffices(officesList);
4467
- })();
4468
- if (regionValue?.key) {
4469
- addressField.onChange?.('');
4470
- }
4471
- }, [regionValue.key]);
4472
- const { points } = useOfficesAtmsMapData({
4473
- data: offices,
4474
- filtrationState: {},
4475
- getBalloon: getOfficePoint,
4476
- });
4477
- return (jsxs("div", { children: [jsx(SelectControl, { label: "\u0410\u0434\u0440\u0435\u0441 \u043E\u0442\u0434\u0435\u043B\u0435\u043D\u0438\u044F", placeholder: "\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u043E\u0442\u0434\u0435\u043B\u0435\u043D\u0438\u0435", options: offices.map(({ id, address = '' }) => ({
4478
- key: id?.toString() || '',
4479
- text: address,
4480
- })), ...addressField, isSearch: true }), jsx("div", { className: "h-[600px]", children: jsx(ClientOnly, { children: jsx(YandexMap, { points: points, isLoad: !data, className: "h-full", selectedAddress: addressField?.value?.text }) }) })] }));
4481
- });
4306
+ const DEFAULT_METHODS = {};
4307
+ /**
4308
+ * MobX like reactivity (simplified).
4309
+ * Can be used to migrate from Redux/MobX or something else
4310
+ *
4311
+ * @param store
4312
+ * @returns reactive proxy backed by store
4313
+ */
4314
+ function useStore(store, methods = DEFAULT_METHODS) {
4315
+ const deps = useRef(null);
4316
+ const render = useRerender();
4317
+ useEffect(() => store.bus.watch(ev => {
4318
+ if (deps.current?.has(String(ev.type))) {
4319
+ render();
4320
+ }
4321
+ }), [store, render]);
4322
+ return useMemo(() => new Proxy(methods, {
4323
+ get(_, key) {
4324
+ deps.current ||= new Set();
4325
+ deps.current.add(key);
4326
+ return store.getItem(key);
4327
+ },
4328
+ has(_, key) {
4329
+ deps.current ||= new Set();
4330
+ deps.current.add(key);
4331
+ return store.hasItem(key);
4332
+ },
4333
+ set(_, key, value) {
4334
+ store.setItem(key, value);
4335
+ return true;
4336
+ },
4337
+ deleteProperty(_, key) {
4338
+ store.removeItem(key);
4339
+ return true;
4340
+ }
4341
+ }), [store]);
4342
+ }
4482
4343
 
4483
4344
  const localStore = new Store(); // localStorage cache
4484
4345
  replicate(localStore, new StorageAdapter(globalThis?.localStorage));
@@ -5707,6 +5568,12 @@
5707
5568
 
5708
5569
  const renderSubmitButton = (button, isSending = false) => (jsx(SubmitButton$1, { className: "w-full @xl:w-auto", isLoading: isSending, children: button?.text ? button.text : 'Отправить заявку' }));
5709
5570
 
5571
+ const sessionStore = new Store(); // sessionStorage cache
5572
+ replicate(sessionStore, new StorageAdapter(globalThis?.sessionStorage));
5573
+ function useSessionStore() {
5574
+ return useStore(sessionStore);
5575
+ }
5576
+
5710
5577
  const createDraftTask = async (body) => {
5711
5578
  const res = await fetchRetailJSON('/user-data/createDraftTask', 'POST', body);
5712
5579
  return res || {};
@@ -5735,6 +5602,171 @@
5735
5602
 
5736
5603
  const updateUserTask = (body) => doRequest('/user-data/updateUserTask', 'PUT', body);
5737
5604
 
5605
+ const sendCode = (body) => doRequest('/sms/sendCode', 'POST', body)
5606
+ .then((res) => res.text())
5607
+ .then((text) => text === 'OK');
5608
+
5609
+ const ICON_SIZE = { width: '103', height: '21' };
5610
+
5611
+ const logoTitleSizeStyle = 'text-s';
5612
+
5613
+ const ICON_VERSION_MAP = {
5614
+ 'bg-white': 'color',
5615
+ transparent: 'white',
5616
+ };
5617
+ const SVG_COLOR = {
5618
+ 'bg-white': 'text-primary-main',
5619
+ transparent: 'text-white',
5620
+ };
5621
+ const renderImage = (bgColor, image, size) => {
5622
+ const img = image?.src
5623
+ ? image
5624
+ : {
5625
+ icon: image?.icon || 'LogoIcon',
5626
+ iconVersion: ICON_VERSION_MAP[bgColor],
5627
+ };
5628
+ return (jsx(Img, { image: img, className: SVG_COLOR[bgColor], width: size?.width, height: size?.height }));
5629
+ };
5630
+
5631
+ const TEXT_COLOR = {
5632
+ 'bg-white': 'text-primary-text',
5633
+ transparent: 'text-white',
5634
+ };
5635
+ const Logo = JSX(({ className, href = '/', logo, children, targetBlank, bgColor = 'bg-white', showTitle = true, data, }) => (jsxs("a", { className: style('inline-flex items-center font-sans no-underline', className), href: logo?.href ?? href, target: targetBlank ? '_blank' : '_self', "aria-label": logo?.title ?? 'Россельхозбанк', ...getAspectsAttributes(data), children: [renderImage(bgColor, logo?.image, ICON_SIZE), showTitle
5636
+ ? children ?? (jsx("div", { className: "ml-s", children: jsx(Text, { font: "font-medium", color: TEXT_COLOR[bgColor], size: logoTitleSizeStyle, children: logo?.title ?? 'Россельхозбанк' }) }))
5637
+ : null] })));
5638
+
5639
+ const checkCaptcha = (body) => doRequest('/sms/checkCaptcha', 'POST', body)
5640
+ .then((res) => res.text())
5641
+ .then((text) => text !== 'ERROR');
5642
+
5643
+ const createCaptcha = (phoneNumber) => doRequest(`/sms/createCaptcha?phoneNumber=${encodeURIComponent(phoneNumber)}`, 'GET').then(async (res) => (res ? res.blob() : new Blob()));
5644
+
5645
+ const CaptchaDialog = JSX(({ phoneNumber, sendCode, onClose }) => {
5646
+ const [captcha, setCaptcha] = useState('');
5647
+ const [code, setCode] = useState('');
5648
+ const [hasError, setHasError] = useState(false);
5649
+ const [isLoading, { setTrue: startLoading, setFalse: endLoading }] = useBool(false);
5650
+ const { closeAll } = useDialogManager();
5651
+ const handleCheckCaptcha = useCallback(async () => {
5652
+ startLoading();
5653
+ const isValidCode = await checkCaptcha({ captchaText: code });
5654
+ if (isValidCode) {
5655
+ onClose?.();
5656
+ sendCode?.();
5657
+ }
5658
+ else {
5659
+ setHasError(true);
5660
+ }
5661
+ endLoading();
5662
+ }, [code, sendCode]);
5663
+ const handleCreateCaptcha = useCallback(() => {
5664
+ (async () => setCaptcha(URL.createObjectURL(await createCaptcha(phoneNumber))))();
5665
+ }, []);
5666
+ useEffect(handleCreateCaptcha, []);
5667
+ return (jsx(Dialog, { head: jsx(Logo, {}), onClose: onClose, children: jsxs("div", { className: "flex flex-col gap-lg items-center", children: [jsxs("div", { className: "flex", children: [jsx("img", { className: "grow", src: captcha }), jsx(Button, { className: "w-8", embedded: true, onClick: handleCreateCaptcha, children: jsx(Icon, { iconVersion: "normal", name: "RefreshIcon" }) })] }), jsx(Input, { className: "w-80", onChange: setCode, value: code, placeholder: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043A\u043E\u0434 \u0441 \u043A\u0430\u0440\u0442\u0438\u043D\u043A\u0438" }), hasError ? jsx("div", { className: "text-error", children: "\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u043A\u043E\u0434" }) : null, jsxs("div", { className: "flex w-80 justify-between", children: [jsx(Button, { version: "secondary", onClick: closeAll, children: "\u0412\u0435\u0440\u043D\u0443\u0442\u044C\u0441\u044F" }), jsx(SubmitButton$1, { version: "secondary", disabled: !code, onClick: handleCheckCaptcha, children: "\u041E\u0442\u043F\u0440\u0430\u0432\u0438\u0442\u044C" })] }), isLoading ? jsx(Loader, { blur: false }) : null] }) }));
5668
+ });
5669
+
5670
+ const checkCode = async (body) => fetchRetailJSON('/sms/checkCode', 'POST', body).then(saveToken);
5671
+ const saveToken = (data) => {
5672
+ if (data?.access_token && data?.refresh_token) {
5673
+ globalThis.sessionStorage.setItem('accessToken', data.access_token);
5674
+ globalThis.sessionStorage.setItem('refreshToken', data.refresh_token);
5675
+ }
5676
+ };
5677
+
5678
+ const TIME_TO_RESEND = 180;
5679
+ const useVerifyPhoneDialogSubmit = ({ values, onSuccess, }) => {
5680
+ const sessionStore = useSessionStore();
5681
+ const attempts = sessionStore.smsCode?.attempts || 0;
5682
+ const timer = Math.max(getTimer(sessionStore.smsCode?.sendTime || Date.now()), 0);
5683
+ const [errorText, setErrorText] = useState('');
5684
+ const [isLoading, { setTrue: startLoading, setFalse: endLoading }] = useBool(false);
5685
+ const [timeNextReq, setTimeNextReq] = useState(timer);
5686
+ const resetError = useCallback(() => setErrorText(''), []);
5687
+ const isTimeExpired = Boolean(timeNextReq === 0 && sessionStore.smsCode?.sendTime);
5688
+ const isSubmitButtonDisabled = attempts > 2 || isTimeExpired || !values.every(Boolean);
5689
+ const handleSubmit = useCallback(async () => {
5690
+ try {
5691
+ sessionStore.smsCode = {
5692
+ ...sessionStore.smsCode,
5693
+ attempts: attempts + 1,
5694
+ };
5695
+ startLoading();
5696
+ await checkCode({
5697
+ smsText: values.join(''),
5698
+ smsCodesSetName: { key: 'AUTHENTICATION' },
5699
+ });
5700
+ setTimeNextReq(0);
5701
+ resetError();
5702
+ sessionStore.smsCode = null;
5703
+ onSuccess?.(values.join(''));
5704
+ }
5705
+ catch {
5706
+ setErrorText(attempts > 1 ? 'Исчерпан лимит ввода смс-кода' : 'Неверный код');
5707
+ }
5708
+ finally {
5709
+ endLoading();
5710
+ }
5711
+ }, [values, attempts]);
5712
+ useEffect(() => {
5713
+ if (isTimeExpired) {
5714
+ setErrorText('Код просрочен');
5715
+ }
5716
+ else if (attempts > 2) {
5717
+ setErrorText('Исчерпан лимит ввода смс-кода');
5718
+ }
5719
+ }, [isTimeExpired]);
5720
+ return {
5721
+ handleSubmit,
5722
+ hasError: Boolean(errorText),
5723
+ errorText,
5724
+ isLoading,
5725
+ timeNextReq,
5726
+ isSubmitButtonDisabled,
5727
+ setTimeNextReq,
5728
+ setErrorText,
5729
+ };
5730
+ };
5731
+
5732
+ const CODE_LENGTH = 4;
5733
+ const VerifyPhoneDialog = JSX(({ phone, withDescription = true, consents, onSuccess = noop, onClose = noop }) => {
5734
+ const [values, setValues] = useState(Array(CODE_LENGTH).fill(''));
5735
+ const sessionStore = useSessionStore();
5736
+ const { handleSubmit, hasError, errorText, isLoading, timeNextReq, isSubmitButtonDisabled, setTimeNextReq, setErrorText, } = useVerifyPhoneDialogSubmit({
5737
+ values,
5738
+ onSuccess,
5739
+ });
5740
+ const captchaDialog = useDialog(CaptchaDialog);
5741
+ const phoneNumber = formatPhone(phone);
5742
+ const restartTimer = useCountDownTimer({ seconds: timeNextReq, onTick: setTimeNextReq });
5743
+ const handleSendCode = useCallback(async () => {
5744
+ const response = await sendCode({
5745
+ phoneNumber,
5746
+ smsCodesSetName: { key: 'AUTHENTICATION' },
5747
+ });
5748
+ if (response) {
5749
+ setTimeNextReq(TIME_TO_RESEND);
5750
+ restartTimer(TIME_TO_RESEND);
5751
+ setErrorText('');
5752
+ sessionStore.smsCode = {
5753
+ sendTime: Date.now(),
5754
+ attempts: 0,
5755
+ };
5756
+ }
5757
+ else {
5758
+ captchaDialog.open({ phoneNumber, sendCode: handleSendCode });
5759
+ }
5760
+ }, [phoneNumber, restartTimer, onClose]);
5761
+ useEffect(() => {
5762
+ if (!sessionStore.smsCode?.sendTime) {
5763
+ handleSendCode();
5764
+ }
5765
+ }, []);
5766
+ return (jsx(VerifyPhoneDialogLayout, { isSubmitButtonDisabled: isSubmitButtonDisabled, onSubmit: handleSubmit, onSendCode: handleSendCode, timeNextReq: timeNextReq, values: values, onChange: setValues, phone: phone, isLoading: isLoading, errorText: errorText, hasError: hasError, onClose: onClose, children: renderDescription$2(consents, withDescription) }));
5767
+ });
5768
+ const renderDescription$2 = (consents, isRender = false) => isRender ? (jsxs(RichText, { itemSize: "list-s", children: [jsx("span", { children: "\u0412\u0432\u043E\u0434\u044F \u043A\u043E\u0434, \u044F \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u044E, \u0447\u0442\u043E \u043E\u0437\u043D\u0430\u043A\u043E\u043C\u043B\u0435\u043D \u0438 \u043F\u043E\u0434\u043F\u0438\u0441\u044B\u0432\u0430\u044E: " }), jsx("ul", { children: consents?.map((_, i) => (jsx("li", { children: _ }, `${_}-${i}`))) })] })) : null;
5769
+
5738
5770
  const defaultConsentText = {
5739
5771
  title: 'Подпишите согласие на запрос в БКИ',
5740
5772
  description: 'Согласие на запрос в Бюро кредитных историй (БКИ) ускорит решение по кредиту',
@@ -10373,7 +10405,7 @@
10373
10405
  };
10374
10406
  const handleInputSell = (setCalcState, currencyRatesSell) => (value, currencyFrom, currencyTo) => {
10375
10407
  setCalcState({ inputSell: formatValue(value), selectBuy: currencyTo });
10376
- const rate = currencyRatesSell.find((_) => _.currency?.currency === currencyTo)?.buyExchangeRate ||
10408
+ const rate = currencyRatesSell.find((_) => _.currency?.currency === currencyTo)?.saleExchangeRate ||
10377
10409
  currencyRatesSell.find((_) => _.currency?.currency === currencyFrom)?.buyExchangeRate;
10378
10410
  if (rate) {
10379
10411
  setCalcState({
@@ -10383,7 +10415,7 @@
10383
10415
  };
10384
10416
  const handleInputBuy = (setCalcState, currencyRatesBuy) => (value, currencyTo, currencyFrom) => {
10385
10417
  setCalcState({ inputBuy: formatValue(value), selectSell: currencyFrom });
10386
- const rate = currencyRatesBuy.find((_) => _.currency?.currency === currencyFrom)?.saleExchangeRate ||
10418
+ const rate = currencyRatesBuy.find((_) => _.currency?.currency === currencyFrom)?.buyExchangeRate ||
10387
10419
  currencyRatesBuy.find((_) => _.currency?.currency === currencyTo)?.saleExchangeRate;
10388
10420
  if (rate) {
10389
10421
  setCalcState({
@@ -11752,7 +11784,7 @@
11752
11784
  slots: () => [HEADER_SLOT, FOOTER_SLOT, STICKY_FOOTER_SLOT],
11753
11785
  });
11754
11786
 
11755
- const packageVersion = "0.14.910";
11787
+ const packageVersion = "0.14.912";
11756
11788
 
11757
11789
  exports.Blocks = Blocks;
11758
11790
  exports.ContentPage = ContentPage;