@akinon/next 2.0.0-beta.2 → 2.0.0-beta.20

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 (189) hide show
  1. package/.eslintrc.js +12 -0
  2. package/CHANGELOG.md +377 -7
  3. package/__tests__/next-config.test.ts +83 -0
  4. package/__tests__/tsconfig.json +23 -0
  5. package/api/auth.ts +133 -44
  6. package/api/barcode-search.ts +59 -0
  7. package/api/cache.ts +41 -5
  8. package/api/client.ts +21 -4
  9. package/api/form.ts +85 -0
  10. package/api/image-proxy.ts +75 -0
  11. package/api/product-categories.ts +53 -0
  12. package/api/similar-product-list.ts +63 -0
  13. package/api/similar-products.ts +111 -0
  14. package/api/virtual-try-on.ts +382 -0
  15. package/assets/styles/index.scss +84 -0
  16. package/babel.config.js +6 -0
  17. package/bin/pz-generate-routes.js +115 -0
  18. package/bin/pz-prebuild.js +1 -0
  19. package/bin/pz-predev.js +1 -0
  20. package/bin/pz-run-tests.js +99 -0
  21. package/bin/run-prebuild-tests.js +46 -0
  22. package/components/accordion.tsx +20 -5
  23. package/components/button.tsx +51 -36
  24. package/components/client-root.tsx +138 -2
  25. package/components/file-input.tsx +65 -3
  26. package/components/index.ts +1 -0
  27. package/components/input.tsx +1 -1
  28. package/components/link.tsx +46 -16
  29. package/components/logger-popup.tsx +213 -0
  30. package/components/modal.tsx +32 -16
  31. package/components/plugin-module.tsx +62 -3
  32. package/components/price.tsx +2 -2
  33. package/components/select.tsx +1 -1
  34. package/components/selected-payment-option-view.tsx +21 -0
  35. package/components/theme-editor/blocks/accordion-block.tsx +136 -0
  36. package/components/theme-editor/blocks/block-renderer-registry.tsx +77 -0
  37. package/components/theme-editor/blocks/button-block.tsx +593 -0
  38. package/components/theme-editor/blocks/counter-block.tsx +348 -0
  39. package/components/theme-editor/blocks/divider-block.tsx +20 -0
  40. package/components/theme-editor/blocks/embed-block.tsx +208 -0
  41. package/components/theme-editor/blocks/group-block.tsx +116 -0
  42. package/components/theme-editor/blocks/hotspot-block.tsx +147 -0
  43. package/components/theme-editor/blocks/icon-block.tsx +230 -0
  44. package/components/theme-editor/blocks/image-block.tsx +137 -0
  45. package/components/theme-editor/blocks/image-gallery-block.tsx +269 -0
  46. package/components/theme-editor/blocks/input-block.tsx +123 -0
  47. package/components/theme-editor/blocks/link-block.tsx +216 -0
  48. package/components/theme-editor/blocks/lottie-block.tsx +325 -0
  49. package/components/theme-editor/blocks/map-block.tsx +89 -0
  50. package/components/theme-editor/blocks/slider-block.tsx +595 -0
  51. package/components/theme-editor/blocks/tab-block.tsx +10 -0
  52. package/components/theme-editor/blocks/text-block.tsx +52 -0
  53. package/components/theme-editor/blocks/video-block.tsx +122 -0
  54. package/components/theme-editor/components/action-toolbar.tsx +305 -0
  55. package/components/theme-editor/components/designer-overlay.tsx +74 -0
  56. package/components/theme-editor/components/with-designer-features.tsx +142 -0
  57. package/components/theme-editor/dynamic-font-loader.tsx +79 -0
  58. package/components/theme-editor/hooks/use-designer-features.tsx +100 -0
  59. package/components/theme-editor/hooks/use-external-designer.tsx +95 -0
  60. package/components/theme-editor/hooks/use-native-widget-data.ts +188 -0
  61. package/components/theme-editor/hooks/use-visibility-context.ts +27 -0
  62. package/components/theme-editor/placeholder-registry.ts +31 -0
  63. package/components/theme-editor/sections/before-after-section.tsx +245 -0
  64. package/components/theme-editor/sections/contact-form-section.tsx +563 -0
  65. package/components/theme-editor/sections/countdown-campaign-banner-section.tsx +433 -0
  66. package/components/theme-editor/sections/coupon-banner-section.tsx +710 -0
  67. package/components/theme-editor/sections/divider-section.tsx +62 -0
  68. package/components/theme-editor/sections/featured-product-spotlight-section.tsx +507 -0
  69. package/components/theme-editor/sections/find-in-store-section.tsx +1995 -0
  70. package/components/theme-editor/sections/hover-showcase-section.tsx +326 -0
  71. package/components/theme-editor/sections/image-hotspot-section.tsx +142 -0
  72. package/components/theme-editor/sections/installment-options-section.tsx +1065 -0
  73. package/components/theme-editor/sections/notification-banner-section.tsx +173 -0
  74. package/components/theme-editor/sections/order-tracking-lookup-section.tsx +1379 -0
  75. package/components/theme-editor/sections/posts-slider-section.tsx +472 -0
  76. package/components/theme-editor/sections/pre-order-launch-banner-section.tsx +663 -0
  77. package/components/theme-editor/sections/section-renderer-registry.tsx +89 -0
  78. package/components/theme-editor/sections/section-wrapper.tsx +135 -0
  79. package/components/theme-editor/sections/shipping-threshold-progress-section.tsx +586 -0
  80. package/components/theme-editor/sections/stats-counter-section.tsx +486 -0
  81. package/components/theme-editor/sections/tabs-section.tsx +578 -0
  82. package/components/theme-editor/theme-block.tsx +102 -0
  83. package/components/theme-editor/theme-placeholder-client.tsx +218 -0
  84. package/components/theme-editor/theme-placeholder-wrapper.tsx +732 -0
  85. package/components/theme-editor/theme-placeholder.tsx +288 -0
  86. package/components/theme-editor/theme-section.tsx +1224 -0
  87. package/components/theme-editor/theme-settings-context.tsx +13 -0
  88. package/components/theme-editor/utils/index.ts +792 -0
  89. package/components/theme-editor/utils/iterator-utils.ts +234 -0
  90. package/components/theme-editor/utils/publish-window.ts +86 -0
  91. package/components/theme-editor/utils/visibility-rules.ts +188 -0
  92. package/data/client/account.ts +17 -2
  93. package/data/client/api.ts +2 -0
  94. package/data/client/basket.ts +66 -5
  95. package/data/client/checkout.ts +391 -99
  96. package/data/client/misc.ts +38 -2
  97. package/data/client/product.ts +19 -2
  98. package/data/client/user.ts +16 -8
  99. package/data/server/category.ts +11 -9
  100. package/data/server/flatpage.ts +11 -4
  101. package/data/server/form.ts +15 -4
  102. package/data/server/landingpage.ts +11 -4
  103. package/data/server/list.ts +5 -4
  104. package/data/server/menu.ts +11 -3
  105. package/data/server/product.ts +111 -55
  106. package/data/server/seo.ts +14 -4
  107. package/data/server/special-page.ts +5 -4
  108. package/data/server/widget.ts +90 -5
  109. package/data/urls.ts +16 -5
  110. package/hocs/client/with-segment-defaults.tsx +2 -2
  111. package/hocs/server/with-segment-defaults.tsx +65 -20
  112. package/hooks/index.ts +4 -0
  113. package/hooks/use-localization.ts +24 -10
  114. package/hooks/use-logger-context.tsx +114 -0
  115. package/hooks/use-logger.ts +92 -0
  116. package/hooks/use-loyalty-availability.ts +21 -0
  117. package/hooks/use-payment-options.ts +2 -1
  118. package/hooks/use-pz-params.ts +37 -0
  119. package/hooks/use-router.ts +51 -14
  120. package/hooks/use-sentry-uncaught-errors.ts +24 -0
  121. package/instrumentation/index.ts +10 -1
  122. package/instrumentation/node.ts +2 -20
  123. package/jest.config.js +25 -0
  124. package/lib/cache-handler.mjs +534 -16
  125. package/lib/cache.ts +272 -37
  126. package/localization/index.ts +2 -1
  127. package/localization/provider.tsx +2 -5
  128. package/middlewares/bfcache-headers.ts +18 -0
  129. package/middlewares/checkout-provider.ts +1 -1
  130. package/middlewares/complete-gpay.ts +32 -26
  131. package/middlewares/complete-masterpass.ts +33 -26
  132. package/middlewares/complete-wallet.ts +182 -0
  133. package/middlewares/default.ts +360 -215
  134. package/middlewares/index.ts +10 -2
  135. package/middlewares/locale.ts +34 -11
  136. package/middlewares/masterpass-rest-callback.ts +230 -0
  137. package/middlewares/oauth-login.ts +200 -57
  138. package/middlewares/pretty-url.ts +21 -8
  139. package/middlewares/redirection-payment.ts +32 -26
  140. package/middlewares/saved-card-redirection.ts +33 -26
  141. package/middlewares/three-d-redirection.ts +32 -26
  142. package/middlewares/url-redirection.ts +11 -1
  143. package/middlewares/wallet-complete-redirection.ts +206 -0
  144. package/package.json +25 -10
  145. package/plugins.d.ts +19 -4
  146. package/plugins.js +10 -1
  147. package/redux/actions.ts +47 -0
  148. package/redux/middlewares/checkout.ts +63 -138
  149. package/redux/middlewares/index.ts +14 -10
  150. package/redux/middlewares/pre-order/address.ts +7 -2
  151. package/redux/middlewares/pre-order/attribute-based-shipping-option.ts +7 -1
  152. package/redux/middlewares/pre-order/data-source-shipping-option.ts +7 -1
  153. package/redux/middlewares/pre-order/delivery-option.ts +7 -1
  154. package/redux/middlewares/pre-order/index.ts +16 -10
  155. package/redux/middlewares/pre-order/installment-option.ts +8 -1
  156. package/redux/middlewares/pre-order/payment-option-reset.ts +37 -0
  157. package/redux/middlewares/pre-order/payment-option.ts +7 -1
  158. package/redux/middlewares/pre-order/pre-order-validation.ts +8 -3
  159. package/redux/middlewares/pre-order/redirection.ts +8 -2
  160. package/redux/middlewares/pre-order/set-pre-order.ts +6 -2
  161. package/redux/middlewares/pre-order/shipping-option.ts +7 -1
  162. package/redux/middlewares/pre-order/shipping-step.ts +5 -1
  163. package/redux/reducers/checkout.ts +23 -3
  164. package/redux/reducers/index.ts +11 -3
  165. package/redux/reducers/root.ts +7 -2
  166. package/redux/reducers/widget.ts +80 -0
  167. package/sentry/index.ts +69 -13
  168. package/tailwind/content.js +16 -0
  169. package/types/commerce/account.ts +5 -1
  170. package/types/commerce/checkout.ts +35 -1
  171. package/types/commerce/widget.ts +33 -0
  172. package/types/index.ts +101 -6
  173. package/types/next-auth.d.ts +2 -2
  174. package/types/widget.ts +80 -0
  175. package/utils/app-fetch.ts +7 -2
  176. package/utils/generate-commerce-search-params.ts +3 -2
  177. package/utils/get-checkout-path.ts +3 -0
  178. package/utils/get-root-hostname.ts +28 -0
  179. package/utils/index.ts +64 -10
  180. package/utils/localization.ts +4 -0
  181. package/utils/mobile-3d-iframe.ts +8 -2
  182. package/utils/override-middleware.ts +7 -12
  183. package/utils/pz-segments.ts +92 -0
  184. package/utils/redirect-ignore.ts +35 -0
  185. package/utils/redirect.ts +9 -3
  186. package/utils/redirection-iframe.ts +8 -2
  187. package/utils/widget-styles.ts +107 -0
  188. package/views/error-page.tsx +93 -0
  189. package/with-pz-config.js +13 -6
@@ -2,6 +2,7 @@ import {
2
2
  setBankAccounts,
3
3
  setCardType,
4
4
  setInstallmentOptions,
5
+ setPaymentOptions,
5
6
  setPaymentStepBusy,
6
7
  setSelectedBankAccountPk,
7
8
  setSelectedCreditPaymentPk,
@@ -9,15 +10,18 @@ import {
9
10
  } from '../../redux/reducers/checkout';
10
11
  import {
11
12
  CheckoutContext,
13
+ AccountUsage,
12
14
  ExtraField,
13
15
  GuestLoginFormParams,
14
16
  Order,
15
- PreOrder
17
+ PreOrder,
18
+ SendSmsType,
19
+ VerifySmsType
16
20
  } from '../../types';
17
- import { buildClientRequestUrl } from '../../utils';
21
+ import { buildClientRequestUrl, getCookie } from '../../utils';
18
22
  import { api } from './api';
19
23
  import { checkout } from '../urls';
20
- import { AppDispatch, AppStore, store } from 'redux/store';
24
+ import type { AppDispatch, AppStore } from 'redux/store';
21
25
  import settings from 'settings';
22
26
  import { showMobile3dIframe } from '../../utils/mobile-3d-iframe';
23
27
  import {
@@ -29,6 +33,21 @@ import {
29
33
  buildDirectPurchaseForm,
30
34
  buildPurchaseForm
31
35
  } from '@akinon/pz-masterpass/src/utils';
36
+ import { devLogger } from '@akinon/next/hooks/use-logger-context';
37
+ import { LogLevel } from '@akinon/next/hooks/use-logger';
38
+
39
+ const getLatestState = async (getState: () => any): Promise<any> => {
40
+ await new Promise((resolve) => setTimeout(resolve, 250));
41
+
42
+ return getState();
43
+ };
44
+
45
+ const recentLogMessages = new Map<string, number>();
46
+
47
+ const getStore = async (): Promise<AppStore> => {
48
+ const { store } = await import('redux/store');
49
+ return store;
50
+ };
32
51
 
33
52
  interface CheckoutResponse {
34
53
  pre_order?: PreOrder;
@@ -40,6 +59,53 @@ interface CheckoutResponse {
40
59
  redirect_url?: string;
41
60
  }
42
61
 
62
+ const validateCheckoutState = (
63
+ state: any,
64
+ validations: Array<{
65
+ condition: (state: any) => boolean;
66
+ errorMessage: string;
67
+ severity?: LogLevel;
68
+ data?: any;
69
+ action?: () => void;
70
+ }>
71
+ ) => {
72
+ validations.forEach(
73
+ ({
74
+ condition,
75
+ errorMessage,
76
+ severity = 'error',
77
+ data: logData,
78
+ action
79
+ }) => {
80
+ if (condition(state)) {
81
+ const now = Date.now();
82
+ const lastLogged = recentLogMessages.get(errorMessage) || 0;
83
+
84
+ if (now - lastLogged > 2000) {
85
+ recentLogMessages.set(errorMessage, now);
86
+
87
+ switch (severity) {
88
+ case 'error':
89
+ devLogger.error(
90
+ errorMessage,
91
+ logData || state.checkout?.preOrder
92
+ );
93
+ action?.();
94
+ break;
95
+ case 'warn':
96
+ devLogger.warn(errorMessage, logData || state.checkout?.preOrder);
97
+ action?.();
98
+ break;
99
+ default:
100
+ devLogger.info(errorMessage, logData || state.checkout?.preOrder);
101
+ action?.();
102
+ }
103
+ }
104
+ }
105
+ }
106
+ );
107
+ };
108
+
43
109
  interface SetAddressesParams {
44
110
  shippingAddressPk: number;
45
111
  billingAddressPk: number;
@@ -79,6 +145,18 @@ export interface PayOnDeliveryParams {
79
145
  paymentType: string;
80
146
  }
81
147
 
148
+ const buildCheckoutRequestUrl = (
149
+ path: string,
150
+ options?: Parameters<typeof buildClientRequestUrl>[1]
151
+ ) => {
152
+ const effectivePath =
153
+ getCookie('pz-post-checkout-flow') === 'true' &&
154
+ /^\/orders\/checkout(\/|\?|$)/.test(path)
155
+ ? path.replace('/orders/checkout', '/orders/post-checkout')
156
+ : path;
157
+ return buildClientRequestUrl(effectivePath, options);
158
+ };
159
+
82
160
  const completeMasterpassPayment = async (
83
161
  params: CompleteCreditCardParams,
84
162
  dispatch: AppDispatch,
@@ -174,40 +252,59 @@ const completeMasterpassPayment = async (
174
252
  });
175
253
  };
176
254
 
255
+ let checkoutAbortController = new AbortController();
256
+
257
+ export const getCheckoutAbortSignal = () => {
258
+ if (checkoutAbortController.signal.aborted) {
259
+ checkoutAbortController = new AbortController();
260
+ }
261
+ return checkoutAbortController.signal;
262
+ };
263
+
264
+ const abortCheckout = () => {
265
+ checkoutAbortController.abort();
266
+ checkoutAbortController = new AbortController();
267
+ };
268
+
177
269
  export const checkoutApi = api.injectEndpoints({
178
270
  endpoints: (build) => ({
179
271
  fetchCheckout: build.query<CheckoutResponse, void>({
180
272
  query: () => ({
181
- url: buildClientRequestUrl(checkout.fetchCheckout, {})
273
+ url: buildCheckoutRequestUrl(checkout.fetchCheckout, {})
182
274
  }),
183
275
  providesTags: ['Checkout']
184
276
  }),
277
+ resetCheckoutState: build.query<CheckoutResponse, void>({
278
+ query: () => ({
279
+ url: buildCheckoutRequestUrl(checkout.guestLogin, {})
280
+ })
281
+ }),
185
282
  fetchCheckoutResult: build.query<{ order: Order }, string>({
186
283
  query: (token: string) =>
187
- buildClientRequestUrl(checkout.fetchCheckoutResult(token))
284
+ buildCheckoutRequestUrl(checkout.fetchCheckoutResult(token))
188
285
  }),
189
286
  get3dRedirectForm: build.query<{ result: string }, void>({
190
287
  query: () =>
191
- buildClientRequestUrl(checkout.get3dRedirectForm, {
288
+ buildCheckoutRequestUrl(checkout.get3dRedirectForm, {
192
289
  responseType: 'text'
193
290
  })
194
291
  }),
195
292
  getContract: build.query<GetContractResponse, string>({
196
293
  query: (slug: string) =>
197
- buildClientRequestUrl(checkout.getContract(slug), {
294
+ buildCheckoutRequestUrl(checkout.getContract(slug), {
198
295
  responseType: 'text'
199
296
  })
200
297
  }),
201
298
  getCoupons: build.query<CheckoutResponse, void>({
202
299
  query: () => ({
203
- url: buildClientRequestUrl(checkout.couponSelectionPage)
300
+ url: buildCheckoutRequestUrl(checkout.couponSelectionPage)
204
301
  })
205
302
  }),
206
303
 
207
304
  setCoupon: build.mutation<CheckoutResponse, { pk: number; action: string }>(
208
305
  {
209
306
  query: ({ pk, action }) => ({
210
- url: buildClientRequestUrl(checkout.couponSelectionPage, {
307
+ url: buildCheckoutRequestUrl(checkout.couponSelectionPage, {
211
308
  useFormData: true
212
309
  }),
213
310
  method: 'POST',
@@ -223,25 +320,32 @@ export const checkoutApi = api.injectEndpoints({
223
320
  CheckoutResponse,
224
321
  CompleteCreditCardParams
225
322
  >({
226
- query: ({
227
- card_holder,
228
- card_cvv,
229
- card_number,
230
- card_month,
231
- card_year,
232
- use_three_d = true,
233
- save = undefined
234
- }) => {
323
+ async queryFn(
324
+ {
325
+ card_holder,
326
+ card_cvv,
327
+ card_number,
328
+ card_month,
329
+ card_year,
330
+ use_three_d = true,
331
+ save = undefined
332
+ },
333
+ _queryApi,
334
+ _extraOptions,
335
+ baseQuery
336
+ ) {
337
+ const reduxStore = await getStore();
235
338
  const paymentOption =
236
- store.getState().checkout?.preOrder?.payment_option;
339
+ reduxStore.getState().checkout?.preOrder?.payment_option;
237
340
 
238
341
  if (paymentOption?.payment_type === 'masterpass') {
239
- return {
240
- url: buildClientRequestUrl(checkout.getMasterpassOrderNo, {
342
+ const result = await baseQuery({
343
+ url: buildCheckoutRequestUrl(checkout.getMasterpassOrderNo, {
241
344
  useFormData: true
242
345
  }),
243
346
  method: 'POST'
244
- };
347
+ });
348
+ return result as { data: CheckoutResponse } | { error: any };
245
349
  }
246
350
 
247
351
  const body: Record<string, string> = {
@@ -258,18 +362,19 @@ export const checkoutApi = api.injectEndpoints({
258
362
  body.save = save ? '1' : '0';
259
363
  }
260
364
 
261
- return {
262
- url: buildClientRequestUrl(checkout.completeCreditCardPayment, {
365
+ const result = await baseQuery({
366
+ url: buildCheckoutRequestUrl(checkout.completeCreditCardPayment, {
263
367
  useFormData: true
264
368
  }),
265
369
  method: 'POST',
266
370
  body
267
- };
371
+ });
372
+ return result as { data: CheckoutResponse } | { error: any };
268
373
  },
269
374
  async onQueryStarted(args, { dispatch, queryFulfilled }) {
270
375
  dispatch(setPaymentStepBusy(true));
271
376
  const { data } = await queryFulfilled;
272
- const reduxStore = (await import('redux/store')).store;
377
+ const reduxStore = await getStore();
273
378
  const completePaymentContext = data?.context_list?.find(
274
379
  (context) => context?.page_name === 'MasterpassCompletePage'
275
380
  );
@@ -294,7 +399,7 @@ export const checkoutApi = api.injectEndpoints({
294
399
  }
295
400
  >({
296
401
  query: ({ token }) => ({
297
- url: buildClientRequestUrl(checkout.completeMasterpassPayment, {
402
+ url: buildCheckoutRequestUrl(checkout.completeMasterpassPayment, {
298
403
  useFormData: true
299
404
  }),
300
405
  method: 'POST',
@@ -311,7 +416,7 @@ export const checkoutApi = api.injectEndpoints({
311
416
  }),
312
417
  completeFundsTransfer: build.mutation<CheckoutResponse, void>({
313
418
  query: () => ({
314
- url: buildClientRequestUrl(checkout.completeFundsTransfer, {
419
+ url: buildCheckoutRequestUrl(checkout.completeFundsTransfer, {
315
420
  useFormData: true
316
421
  }),
317
422
  method: 'POST',
@@ -328,7 +433,7 @@ export const checkoutApi = api.injectEndpoints({
328
433
  }),
329
434
  guestLogin: build.mutation<CheckoutResponse, GuestLoginFormParams>({
330
435
  query: ({ user_email, phone_number }) => ({
331
- url: buildClientRequestUrl(checkout.guestLogin, {
436
+ url: buildCheckoutRequestUrl(checkout.guestLogin, {
332
437
  useFormData: true
333
438
  }),
334
439
  method: 'POST',
@@ -341,56 +446,132 @@ export const checkoutApi = api.injectEndpoints({
341
446
  }),
342
447
  setDeliveryOption: build.mutation<CheckoutResponse, number>({
343
448
  query: (pk: number) => ({
344
- url: buildClientRequestUrl(checkout.setDeliveryOption, {
449
+ url: buildCheckoutRequestUrl(checkout.setDeliveryOption, {
345
450
  useFormData: true
346
451
  }),
347
452
  method: 'POST',
348
453
  body: {
349
454
  delivery_option: String(pk)
350
- }
455
+ },
456
+ signal: getCheckoutAbortSignal()
351
457
  }),
352
- async onQueryStarted(arg, { dispatch, queryFulfilled }) {
458
+ async onQueryStarted(arg, { dispatch, queryFulfilled, getState }) {
353
459
  dispatch(setShippingStepBusy(true));
354
- await queryFulfilled;
355
- dispatch(setShippingStepBusy(false));
460
+
461
+ const state = await getLatestState(getState);
462
+
463
+ validateCheckoutState(state, [
464
+ {
465
+ condition: (state) => {
466
+ const preOrder = state.checkout?.preOrder;
467
+
468
+ return preOrder?.basket?.basketitem_set?.length === 0;
469
+ },
470
+ errorMessage:
471
+ 'Your shopping basket is empty. Please add items to your basket before selecting a delivery option.',
472
+ action: () => abortCheckout()
473
+ }
474
+ ]);
475
+
476
+ try {
477
+ await queryFulfilled;
478
+ dispatch(setShippingStepBusy(false));
479
+ } catch (error) {
480
+ dispatch(setShippingStepBusy(false));
481
+ }
356
482
  }
357
483
  }),
358
484
  setAddresses: build.mutation<CheckoutResponse, SetAddressesParams>({
359
485
  query: ({ shippingAddressPk, billingAddressPk }) => ({
360
- url: buildClientRequestUrl(checkout.setAddresses, {
486
+ url: buildCheckoutRequestUrl(checkout.setAddresses, {
361
487
  useFormData: true
362
488
  }),
363
489
  method: 'POST',
364
490
  body: {
365
491
  shipping_address: String(shippingAddressPk),
366
492
  billing_address: String(billingAddressPk)
367
- }
493
+ },
494
+ signal: getCheckoutAbortSignal()
368
495
  }),
369
- async onQueryStarted(arg, { dispatch, queryFulfilled }) {
496
+ async onQueryStarted(arg, { dispatch, queryFulfilled, getState }) {
370
497
  dispatch(setShippingStepBusy(true));
371
- await queryFulfilled;
372
- dispatch(setShippingStepBusy(false));
498
+
499
+ const state = await getLatestState(getState);
500
+
501
+ validateCheckoutState(state, [
502
+ {
503
+ condition: (state) => {
504
+ const deliveryOptions = state.checkout?.deliveryOptions;
505
+ const preOrder = state.checkout?.preOrder;
506
+
507
+ return deliveryOptions?.length > 0
508
+ ? preOrder && !preOrder.delivery_option?.pk
509
+ : false;
510
+ },
511
+ errorMessage:
512
+ 'You need to select a delivery option before setting your addresses. Dispatch setAddresses action after delivery option selection.',
513
+ action: () => abortCheckout()
514
+ }
515
+ ]);
516
+
517
+ try {
518
+ await queryFulfilled;
519
+ dispatch(setShippingStepBusy(false));
520
+ } catch (error) {
521
+ dispatch(setShippingStepBusy(false));
522
+ }
373
523
  }
374
524
  }),
375
525
  setShippingOption: build.mutation<CheckoutResponse, number>({
376
526
  query: (pk: number) => ({
377
- url: buildClientRequestUrl(checkout.setShippingOption, {
527
+ url: buildCheckoutRequestUrl(checkout.setShippingOption, {
378
528
  useFormData: true
379
529
  }),
380
530
  method: 'POST',
381
531
  body: {
382
532
  shipping_option: String(pk)
383
- }
533
+ },
534
+ signal: getCheckoutAbortSignal()
384
535
  }),
385
- async onQueryStarted(arg, { dispatch, queryFulfilled }) {
536
+ async onQueryStarted(arg, { dispatch, queryFulfilled, getState }) {
386
537
  dispatch(setShippingStepBusy(true));
387
- await queryFulfilled;
388
- dispatch(setShippingStepBusy(false));
538
+
539
+ const state = await getLatestState(getState);
540
+
541
+ validateCheckoutState(state, [
542
+ {
543
+ condition: (state) => {
544
+ const preOrder = state.checkout?.preOrder;
545
+
546
+ return !preOrder?.billing_address;
547
+ },
548
+ errorMessage:
549
+ 'You need to provide a billing address before selecting a shipping option. Dispatch setShippingOption action after billing address selection.',
550
+ action: () => abortCheckout()
551
+ },
552
+ {
553
+ condition: (state) => {
554
+ const preOrder = state.checkout?.preOrder;
555
+
556
+ return !preOrder?.shipping_address;
557
+ },
558
+ errorMessage:
559
+ 'You need to provide a shipping address before selecting a shipping option. Dispatch setShippingOption action after shipping address selection.',
560
+ action: () => abortCheckout()
561
+ }
562
+ ]);
563
+
564
+ try {
565
+ await queryFulfilled;
566
+ dispatch(setShippingStepBusy(false));
567
+ } catch (error) {
568
+ dispatch(setShippingStepBusy(false));
569
+ }
389
570
  }
390
571
  }),
391
572
  setDataSourceShippingOptions: build.mutation<CheckoutResponse, number[]>({
392
573
  query: (pks) => ({
393
- url: buildClientRequestUrl(checkout.setDataSourceShippingOption, {
574
+ url: buildCheckoutRequestUrl(checkout.setDataSourceShippingOption, {
394
575
  useFormData: true
395
576
  }),
396
577
  method: 'POST',
@@ -406,7 +587,7 @@ export const checkoutApi = api.injectEndpoints({
406
587
  }),
407
588
  setRetailStore: build.mutation<CheckoutResponse, SetRetailStoreParams>({
408
589
  query: ({ retailStorePk, billingAddressPk }) => ({
409
- url: buildClientRequestUrl(
590
+ url: buildCheckoutRequestUrl(
410
591
  '/orders/checkout?page=RetailStoreSelectionPage',
411
592
  {
412
593
  useFormData: true
@@ -419,24 +600,51 @@ export const checkoutApi = api.injectEndpoints({
419
600
  }
420
601
  })
421
602
  }),
603
+ fetchPaymentOptions: build.query<CheckoutResponse, void>({
604
+ query: () => ({
605
+ url: buildCheckoutRequestUrl(checkout.setPaymentOption)
606
+ }),
607
+ providesTags: ['PaymentOptions']
608
+ }),
422
609
  setPaymentOption: build.mutation<CheckoutResponse, number>({
423
610
  query: (pk: number) => ({
424
- url: buildClientRequestUrl(checkout.setPaymentOption, {
611
+ url: buildCheckoutRequestUrl(checkout.setPaymentOption, {
425
612
  useFormData: true
426
613
  }),
427
614
  method: 'POST',
428
615
  body: {
429
616
  payment_option: String(pk)
430
- }
617
+ },
618
+ signal: getCheckoutAbortSignal()
431
619
  }),
432
- async onQueryStarted(arg, { dispatch, queryFulfilled }) {
620
+ async onQueryStarted(arg, { dispatch, queryFulfilled, getState }) {
433
621
  dispatch(setPaymentStepBusy(true));
434
622
  dispatch(setInstallmentOptions([]));
435
623
  dispatch(setBankAccounts([]));
436
624
  dispatch(setSelectedBankAccountPk(null));
437
625
  dispatch(setCardType(null));
438
- await queryFulfilled;
439
- dispatch(setPaymentStepBusy(false));
626
+
627
+ const state = await getLatestState(getState);
628
+
629
+ validateCheckoutState(state, [
630
+ {
631
+ condition: (state) => {
632
+ const preOrder = state.checkout?.preOrder;
633
+
634
+ return !preOrder?.shipping_option?.pk;
635
+ },
636
+ errorMessage:
637
+ 'You need to select a shipping option before choosing a payment method. Dispatch setPaymentOption action after shipping option selection.',
638
+ action: () => abortCheckout()
639
+ }
640
+ ]);
641
+
642
+ try {
643
+ await queryFulfilled;
644
+ dispatch(setPaymentStepBusy(false));
645
+ } catch (error) {
646
+ dispatch(setPaymentStepBusy(false));
647
+ }
440
648
  }
441
649
  }),
442
650
  setWalletSelectionPage: build.mutation<
@@ -447,7 +655,7 @@ export const checkoutApi = api.injectEndpoints({
447
655
  }
448
656
  >({
449
657
  query: ({ payment_option: pk, validationURL }) => ({
450
- url: buildClientRequestUrl(checkout.setWalletSelectionPage, {
658
+ url: buildCheckoutRequestUrl(checkout.setWalletSelectionPage, {
451
659
  useFormData: true
452
660
  }),
453
661
  method: 'POST',
@@ -467,16 +675,15 @@ export const checkoutApi = api.injectEndpoints({
467
675
  CheckoutResponse,
468
676
  {
469
677
  payment_token?: string;
678
+ [key: string]: any;
470
679
  }
471
680
  >({
472
- query: ({ payment_token }) => ({
473
- url: buildClientRequestUrl(checkout.setWalletPaymentPage, {
681
+ query: (requestBody) => ({
682
+ url: buildCheckoutRequestUrl(checkout.setWalletPaymentPage, {
474
683
  useFormData: true
475
684
  }),
476
685
  method: 'POST',
477
- body: {
478
- payment_token
479
- }
686
+ body: requestBody
480
687
  }),
481
688
  async onQueryStarted(arg, { dispatch, queryFulfilled }) {
482
689
  dispatch(setPaymentStepBusy(true));
@@ -484,12 +691,22 @@ export const checkoutApi = api.injectEndpoints({
484
691
  dispatch(setPaymentStepBusy(false));
485
692
  }
486
693
  }),
487
- setWalletCompletePage: build.mutation<CheckoutResponse, boolean>({
488
- query: (success: boolean) => ({
489
- url: buildClientRequestUrl(checkout.setWalletCompletePage),
694
+ setWalletCompletePage: build.mutation<
695
+ CheckoutResponse,
696
+ {
697
+ success: boolean;
698
+ cko_payment_id?: string;
699
+ [key: string]: any;
700
+ }
701
+ >({
702
+ query: ({ success, ...additionalParams }) => ({
703
+ url: buildCheckoutRequestUrl(checkout.setWalletCompletePage, {
704
+ useFormData: true
705
+ }),
490
706
  method: 'POST',
491
707
  body: {
492
- success
708
+ success,
709
+ ...additionalParams
493
710
  }
494
711
  }),
495
712
  async onQueryStarted(arg, { dispatch, queryFulfilled }) {
@@ -499,23 +716,25 @@ export const checkoutApi = api.injectEndpoints({
499
716
  }
500
717
  }),
501
718
  setBinNumber: build.mutation<CheckoutResponse, string>({
502
- query: (binNumber: string) => {
719
+ async queryFn(binNumber, _queryApi, _extraOptions, baseQuery) {
720
+ const reduxStore = await getStore();
503
721
  const paymentOption =
504
- store.getState().checkout?.preOrder?.payment_option;
722
+ reduxStore.getState().checkout?.preOrder?.payment_option;
505
723
  const binNumberUrl =
506
724
  paymentOption?.payment_type === 'masterpass'
507
725
  ? checkout.setMasterpassBinNumber
508
726
  : checkout.setBinNumber;
509
727
 
510
- return {
511
- url: buildClientRequestUrl(binNumberUrl, {
728
+ const result = await baseQuery({
729
+ url: buildCheckoutRequestUrl(binNumberUrl, {
512
730
  useFormData: true
513
731
  }),
514
732
  method: 'POST',
515
733
  body: {
516
734
  bin_number: binNumber
517
735
  }
518
- };
736
+ });
737
+ return result as { data: CheckoutResponse } | { error: any };
519
738
  },
520
739
  async onQueryStarted(arg, { dispatch, queryFulfilled }) {
521
740
  dispatch(setPaymentStepBusy(true));
@@ -525,22 +744,25 @@ export const checkoutApi = api.injectEndpoints({
525
744
  }
526
745
  }),
527
746
  setInstallmentOption: build.mutation<CheckoutResponse, number>({
528
- query: (pk: number) => {
747
+ async queryFn(pk, _queryApi, _extraOptions, baseQuery) {
748
+ const reduxStore = await getStore();
529
749
  const paymentOption =
530
- store.getState().checkout?.preOrder?.payment_option;
750
+ reduxStore.getState().checkout?.preOrder?.payment_option;
531
751
  const installmentOption =
532
752
  paymentOption?.payment_type === 'masterpass'
533
753
  ? checkout.setMasterPassInstallmentOption
534
754
  : checkout.setInstallmentOption;
535
- return {
536
- url: buildClientRequestUrl(installmentOption, {
755
+
756
+ const result = await baseQuery({
757
+ url: buildCheckoutRequestUrl(installmentOption, {
537
758
  useFormData: true
538
759
  }),
539
760
  method: 'POST',
540
761
  body: {
541
762
  installment: String(pk)
542
763
  }
543
- };
764
+ });
765
+ return result as { data: CheckoutResponse } | { error: any };
544
766
  },
545
767
  async onQueryStarted(arg, { dispatch, queryFulfilled }) {
546
768
  dispatch(setPaymentStepBusy(true));
@@ -550,7 +772,7 @@ export const checkoutApi = api.injectEndpoints({
550
772
  }),
551
773
  setFundsTransferOption: build.mutation<CheckoutResponse, number>({
552
774
  query: (pk: number) => ({
553
- url: buildClientRequestUrl(checkout.setFundsTransferOption, {
775
+ url: buildCheckoutRequestUrl(checkout.setFundsTransferOption, {
554
776
  useFormData: true
555
777
  }),
556
778
  method: 'POST',
@@ -567,7 +789,7 @@ export const checkoutApi = api.injectEndpoints({
567
789
  }),
568
790
  setCreditPaymentOption: build.mutation<CheckoutResponse, number>({
569
791
  query: (pk: number) => ({
570
- url: buildClientRequestUrl(checkout.setCreditPaymentOption, {
792
+ url: buildCheckoutRequestUrl(checkout.setCreditPaymentOption, {
571
793
  useFormData: true
572
794
  }),
573
795
  method: 'POST',
@@ -584,7 +806,7 @@ export const checkoutApi = api.injectEndpoints({
584
806
  }),
585
807
  confirmationCreditPayment: build.mutation<CheckoutResponse, void>({
586
808
  query: () => ({
587
- url: buildClientRequestUrl(checkout.confirmationCreditPayment, {
809
+ url: buildCheckoutRequestUrl(checkout.confirmationCreditPayment, {
588
810
  useFormData: true
589
811
  }),
590
812
  method: 'POST',
@@ -600,7 +822,7 @@ export const checkoutApi = api.injectEndpoints({
600
822
  }),
601
823
  completeRedirectionPayment: build.mutation<CheckoutResponse, void>({
602
824
  query: () => ({
603
- url: buildClientRequestUrl(checkout.completeRedirectionPayment, {
825
+ url: buildCheckoutRequestUrl(checkout.completeRedirectionPayment, {
604
826
  useFormData: true
605
827
  }),
606
828
  method: 'POST',
@@ -616,7 +838,7 @@ export const checkoutApi = api.injectEndpoints({
616
838
  }),
617
839
  applePaymentSelect: build.mutation<CheckoutResponse, ApplePaySelectParams>({
618
840
  query: ({ agreement, validationURL }) => ({
619
- url: buildClientRequestUrl(checkout.confirmationPaymentSelect, {
841
+ url: buildCheckoutRequestUrl(checkout.confirmationPaymentSelect, {
620
842
  useFormData: true
621
843
  }),
622
844
  method: 'POST',
@@ -633,7 +855,7 @@ export const checkoutApi = api.injectEndpoints({
633
855
  }),
634
856
  appleQuery: build.mutation<CheckoutResponse, ApplePayQueryParams>({
635
857
  query: ({ agreement, paymentToken }) => ({
636
- url: buildClientRequestUrl(checkout.confirmationQuery, {
858
+ url: buildCheckoutRequestUrl(checkout.confirmationQuery, {
637
859
  useFormData: true
638
860
  }),
639
861
  method: 'POST',
@@ -650,7 +872,7 @@ export const checkoutApi = api.injectEndpoints({
650
872
  }),
651
873
  completeConfirmation: build.mutation<CheckoutResponse, void>({
652
874
  query: () => ({
653
- url: buildClientRequestUrl(checkout.confirmationComplete, {
875
+ url: buildCheckoutRequestUrl(checkout.confirmationComplete, {
654
876
  useFormData: true
655
877
  }),
656
878
  method: 'POST',
@@ -669,7 +891,7 @@ export const checkoutApi = api.injectEndpoints({
669
891
  PayOnDeliveryParams
670
892
  >({
671
893
  query: ({ paymentType }) => ({
672
- url: buildClientRequestUrl(
894
+ url: buildCheckoutRequestUrl(
673
895
  `${checkout.fetchCheckout}?page=PayOnDeliveryPage`,
674
896
  { useFormData: true }
675
897
  ),
@@ -687,7 +909,7 @@ export const checkoutApi = api.injectEndpoints({
687
909
  }),
688
910
  setPayOnDeliveryChoice: build.mutation<CheckoutResponse, string>({
689
911
  query: (body) => ({
690
- url: buildClientRequestUrl(
912
+ url: buildCheckoutRequestUrl(
691
913
  `${checkout.fetchCheckout}?page=PayOnDeliveryPaymentChoicePage`,
692
914
  { useFormData: true }
693
915
  ),
@@ -704,7 +926,7 @@ export const checkoutApi = api.injectEndpoints({
704
926
  }),
705
927
  completeLoyaltyPayment: build.mutation<CheckoutResponse, void>({
706
928
  query: () => ({
707
- url: buildClientRequestUrl(checkout.completeLoyaltyPayment, {
929
+ url: buildCheckoutRequestUrl(checkout.completeLoyaltyPayment, {
708
930
  useFormData: true
709
931
  }),
710
932
  method: 'POST',
@@ -720,23 +942,55 @@ export const checkoutApi = api.injectEndpoints({
720
942
  }),
721
943
  getCheckoutLoyaltyBalance: build.query<CheckoutResponse, void>({
722
944
  query: () => ({
723
- url: buildClientRequestUrl(checkout.loyaltyMoneyUsage)
945
+ url: buildCheckoutRequestUrl(checkout.loyaltyMoneyUsage)
724
946
  })
725
947
  }),
726
- payWithLoyaltyBalance: build.mutation<any, string>({
727
- query: (amount) => ({
728
- url: buildClientRequestUrl(checkout.loyaltyMoneyUsage, {
729
- useFormData: true
730
- }),
731
- method: 'POST',
732
- body: {
733
- loyalty_amount_to_use: amount
948
+ payWithLoyaltyBalance: build.mutation<
949
+ any,
950
+ string | { account_usages: AccountUsage[] }
951
+ >({
952
+ query: (params) => {
953
+ const isAccountUsages =
954
+ typeof params === 'object' && 'account_usages' in params;
955
+
956
+ return {
957
+ url: buildCheckoutRequestUrl(checkout.loyaltyMoneyUsage, {
958
+ useFormData: true
959
+ }),
960
+ method: 'POST',
961
+ body: isAccountUsages
962
+ ? {
963
+ account_usages: JSON.stringify(params.account_usages)
964
+ }
965
+ : {
966
+ loyalty_amount_to_use: params
967
+ }
968
+ };
969
+ },
970
+ async onQueryStarted(arg, { dispatch, queryFulfilled }) {
971
+ dispatch(setPaymentStepBusy(true));
972
+ dispatch(setPaymentOptions([]));
973
+ await queryFulfilled;
974
+
975
+ const paymentOptionsData = await dispatch(
976
+ checkoutApi.endpoints.fetchPaymentOptions.initiate()
977
+ ).unwrap();
978
+
979
+ const paymentOptions = paymentOptionsData?.context_list?.find(
980
+ (context) => context?.page_name === 'PaymentOptionSelectionPage'
981
+ )?.page_context?.payment_options;
982
+
983
+ if (paymentOptions) {
984
+ dispatch(setPaymentOptions(paymentOptions));
734
985
  }
735
- })
986
+
987
+ dispatch(setPaymentStepBusy(false));
988
+ },
989
+ invalidatesTags: ['PaymentOptions']
736
990
  }),
737
991
  setOrderNote: build.mutation<CheckoutResponse, string>({
738
992
  query: (notes) => ({
739
- url: buildClientRequestUrl(checkout.setOrderNote, {
993
+ url: buildCheckoutRequestUrl(checkout.setOrderNote, {
740
994
  useFormData: true
741
995
  }),
742
996
  method: 'POST',
@@ -756,7 +1010,7 @@ export const checkoutApi = api.injectEndpoints({
756
1010
  };
757
1011
 
758
1012
  return {
759
- url: buildClientRequestUrl(checkout.deliveryBagsPage, {
1013
+ url: buildCheckoutRequestUrl(checkout.deliveryBagsPage, {
760
1014
  useFormData: true
761
1015
  }),
762
1016
  method: 'POST',
@@ -769,7 +1023,7 @@ export const checkoutApi = api.injectEndpoints({
769
1023
  Record<string, number>
770
1024
  >({
771
1025
  query: (options) => ({
772
- url: buildClientRequestUrl(checkout.setAttributeBasedShippingOption, {
1026
+ url: buildCheckoutRequestUrl(checkout.setAttributeBasedShippingOption, {
773
1027
  useFormData: true
774
1028
  }),
775
1029
  method: 'POST',
@@ -788,7 +1042,7 @@ export const checkoutApi = api.injectEndpoints({
788
1042
  { extra_field: ExtraField }
789
1043
  >({
790
1044
  query: ({ extra_field }) => ({
791
- url: buildClientRequestUrl(checkout.setOrderSelectionPage, {
1045
+ url: buildCheckoutRequestUrl(checkout.setOrderSelectionPage, {
792
1046
  useFormData: true
793
1047
  }),
794
1048
  method: 'POST',
@@ -799,16 +1053,16 @@ export const checkoutApi = api.injectEndpoints({
799
1053
  }),
800
1054
  fetchLoyaltyData: build.query<CheckoutResponse, void>({
801
1055
  query: () => ({
802
- url: buildClientRequestUrl(checkout.loyaltyCardPage, {
1056
+ url: buildCheckoutRequestUrl(checkout.loyaltyCardPage, {
803
1057
  accept: 'application/json',
804
1058
  contentType: 'application/json'
805
1059
  }),
806
1060
  method: 'GET'
807
1061
  })
808
1062
  }),
809
- setLoyaltyData: build.mutation<CheckoutResponse, number>({
1063
+ setLoyaltyData: build.mutation<CheckoutResponse, number | string>({
810
1064
  query: (amount) => ({
811
- url: buildClientRequestUrl(checkout.loyaltyCardPage, {
1065
+ url: buildCheckoutRequestUrl(checkout.loyaltyCardPage, {
812
1066
  useFormData: true
813
1067
  }),
814
1068
  method: 'POST',
@@ -816,6 +1070,39 @@ export const checkoutApi = api.injectEndpoints({
816
1070
  selected_loyalty_amount: amount
817
1071
  }
818
1072
  })
1073
+ }),
1074
+ sendSms: build.mutation<CheckoutResponse, SendSmsType>({
1075
+ query: (body) => ({
1076
+ url: buildCheckoutRequestUrl(checkout.sendSmsPage, {
1077
+ useFormData: true
1078
+ }),
1079
+ method: 'POST',
1080
+ body
1081
+ })
1082
+ }),
1083
+ verifySms: build.mutation<CheckoutResponse, VerifySmsType>({
1084
+ query: (body) => ({
1085
+ url: buildCheckoutRequestUrl(checkout.verifySmsPage, {
1086
+ useFormData: true
1087
+ }),
1088
+ method: 'POST',
1089
+ body
1090
+ })
1091
+ }),
1092
+ saveSampleProducts: build.mutation<CheckoutResponse, number[] | undefined>({
1093
+ query: (products = []) => {
1094
+ const formData = new FormData();
1095
+
1096
+ products.forEach((product) => {
1097
+ formData.append('sample_products', String(product));
1098
+ });
1099
+
1100
+ return {
1101
+ url: buildCheckoutRequestUrl(checkout.saveSampleProducts),
1102
+ method: 'POST',
1103
+ body: formData
1104
+ };
1105
+ }
819
1106
  })
820
1107
  }),
821
1108
  overrideExisting: false
@@ -835,6 +1122,7 @@ export const {
835
1122
  useSetAddressesMutation,
836
1123
  useSetShippingOptionMutation,
837
1124
  useSetDataSourceShippingOptionsMutation,
1125
+ useFetchPaymentOptionsQuery,
838
1126
  useSetPaymentOptionMutation,
839
1127
  useSetBinNumberMutation,
840
1128
  useSetInstallmentOptionMutation,
@@ -859,5 +1147,9 @@ export const {
859
1147
  useSetLoyaltyDataMutation,
860
1148
  useSetWalletSelectionPageMutation,
861
1149
  useSetWalletPaymentPageMutation,
862
- useSetWalletCompletePageMutation
1150
+ useSetWalletCompletePageMutation,
1151
+ useSendSmsMutation,
1152
+ useVerifySmsMutation,
1153
+ useResetCheckoutStateQuery,
1154
+ useSaveSampleProductsMutation
863
1155
  } = checkoutApi;