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