@akinon/next 2.0.0-beta.1 → 2.0.0-beta.11

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 (74) hide show
  1. package/.eslintrc.js +12 -0
  2. package/CHANGELOG.md +56 -0
  3. package/__tests__/next-config.test.ts +83 -0
  4. package/__tests__/tsconfig.json +23 -0
  5. package/api/auth.ts +72 -5
  6. package/api/client.ts +18 -1
  7. package/babel.config.js +6 -0
  8. package/bin/pz-prebuild.js +1 -0
  9. package/bin/pz-run-tests.js +99 -0
  10. package/bin/run-prebuild-tests.js +46 -0
  11. package/components/accordion.tsx +1 -1
  12. package/components/button.tsx +51 -36
  13. package/components/client-root.tsx +20 -0
  14. package/components/input.tsx +1 -1
  15. package/components/modal.tsx +1 -1
  16. package/components/price.tsx +2 -2
  17. package/components/select.tsx +1 -1
  18. package/components/selected-payment-option-view.tsx +1 -1
  19. package/data/client/api.ts +2 -0
  20. package/data/client/basket.ts +27 -5
  21. package/data/client/checkout.ts +62 -7
  22. package/data/client/misc.ts +25 -1
  23. package/data/client/product.ts +19 -2
  24. package/data/client/user.ts +16 -8
  25. package/data/server/flatpage.ts +8 -4
  26. package/data/server/form.ts +12 -4
  27. package/data/server/landingpage.ts +8 -4
  28. package/data/server/menu.ts +7 -2
  29. package/data/server/product.ts +16 -5
  30. package/data/server/seo.ts +11 -4
  31. package/data/server/widget.ts +19 -4
  32. package/data/urls.ts +11 -3
  33. package/hooks/index.ts +1 -0
  34. package/hooks/use-localization.ts +24 -10
  35. package/hooks/use-router.ts +5 -2
  36. package/hooks/use-sentry-uncaught-errors.ts +24 -0
  37. package/instrumentation/index.ts +10 -0
  38. package/jest.config.js +19 -0
  39. package/lib/cache-handler.mjs +2 -2
  40. package/lib/cache.ts +4 -4
  41. package/localization/index.ts +2 -1
  42. package/middlewares/default.ts +31 -4
  43. package/middlewares/locale.ts +35 -11
  44. package/middlewares/url-redirection.ts +16 -0
  45. package/package.json +8 -4
  46. package/plugins.js +2 -1
  47. package/redux/middlewares/checkout.ts +30 -130
  48. package/redux/middlewares/index.ts +6 -2
  49. package/redux/middlewares/pre-order/address.ts +50 -0
  50. package/redux/middlewares/pre-order/attribute-based-shipping-option.ts +46 -0
  51. package/redux/middlewares/pre-order/data-source-shipping-option.ts +43 -0
  52. package/redux/middlewares/pre-order/delivery-option.ts +40 -0
  53. package/redux/middlewares/pre-order/index.ts +29 -0
  54. package/redux/middlewares/pre-order/installment-option.ts +42 -0
  55. package/redux/middlewares/pre-order/payment-option.ts +34 -0
  56. package/redux/middlewares/pre-order/pre-order-validation.ts +24 -0
  57. package/redux/middlewares/pre-order/redirection.ts +40 -0
  58. package/redux/middlewares/pre-order/set-pre-order.ts +22 -0
  59. package/redux/middlewares/pre-order/shipping-option.ts +44 -0
  60. package/redux/middlewares/pre-order/shipping-step.ts +38 -0
  61. package/redux/reducers/checkout.ts +8 -2
  62. package/redux/reducers/index.ts +5 -3
  63. package/redux/reducers/root.ts +7 -2
  64. package/sentry/index.ts +36 -17
  65. package/tailwind/content.js +16 -0
  66. package/types/commerce/account.ts +5 -1
  67. package/types/commerce/checkout.ts +23 -0
  68. package/types/index.ts +7 -2
  69. package/utils/get-root-hostname.ts +28 -0
  70. package/utils/index.ts +6 -2
  71. package/utils/localization.ts +4 -0
  72. package/utils/override-middleware.ts +25 -0
  73. package/views/error-page.tsx +93 -0
  74. package/with-pz-config.js +4 -3
package/data/urls.ts CHANGED
@@ -74,7 +74,10 @@ export const address = {
74
74
 
75
75
  export const basket = {
76
76
  getBasket: '/baskets/basket/',
77
+ getMiniBasket: '/baskets/basket/mini/',
77
78
  getBasketDetail: (namespace: string) => `/baskets/basket/${namespace}/`,
79
+ getMiniBasketDetail: (namespace: string) =>
80
+ `/baskets/basket/${namespace}/mini/`,
78
81
  getAllBaskets: '/baskets/basket-list/',
79
82
  removeBasket: (pk: number) => `/baskets/basket-list/${pk}/`,
80
83
  selectMainBasket: (pk: number) => `/baskets/basket-list/id/${pk}/main/`,
@@ -137,7 +140,9 @@ export const checkout = {
137
140
  '/orders/checkout/?page=AttributeBasedShippingOptionSelectionPage',
138
141
  deliveryBagsPage: '/orders/checkout/?page=DeliveryBagsPage',
139
142
  setOrderSelectionPage: '/orders/checkout/?page=OrderSelectionPage',
140
- loyaltyCardPage: '/orders/checkout/?page=LoyaltyCardPage'
143
+ loyaltyCardPage: '/orders/checkout/?page=LoyaltyCardPage',
144
+ sendSmsPage: '/orders/checkout/?page=SendSmsPage',
145
+ verifySmsPage: '/orders/checkout/?page=VerifySmsPage'
141
146
  };
142
147
 
143
148
  export const flatpage = {
@@ -160,7 +165,8 @@ export const misc = {
160
165
  parent ? `&parent=${parent}` : ''
161
166
  }`,
162
167
  cmsSeo: (slug: string | string[]) => `/cms/seo/?url=${slug ? slug : '/'}`,
163
- setCurrency: '/users/activate-currency/'
168
+ setCurrency: '/users/activate-currency/',
169
+ bukalemunImageUrl: (params: string) => `/bukalemun/?${params}`
164
170
  };
165
171
 
166
172
  export const product = {
@@ -174,7 +180,9 @@ export const product = {
174
180
  slug: (slug: string) => `/${slug}/`,
175
181
  categoryUrl: (pk: number) => `/products/${pk}/category_nodes/?limit=1`,
176
182
  breadcrumbUrl: (menuitemmodel: string) =>
177
- `/menus/generate_breadcrumb/?item=${menuitemmodel}&generator_name=menu_item`
183
+ `/menus/generate_breadcrumb/?item=${menuitemmodel}&generator_name=menu_item`,
184
+ bundleProduct: (productPk: string, queryString: string) =>
185
+ `/bundle-product/${productPk}/?${queryString}`
178
186
  };
179
187
 
180
188
  export const wishlist = {
package/hooks/index.ts CHANGED
@@ -10,3 +10,4 @@ export * from './use-mobile-iframe-handler';
10
10
  export * from './use-payment-options';
11
11
  export * from './use-pagination';
12
12
  export * from './use-message-listener';
13
+ export * from './use-sentry-uncaught-errors';
@@ -25,19 +25,33 @@ export const useLocalization = () => {
25
25
  * @param locale Locale value defined in the settings.
26
26
  */
27
27
  const setLocale = (locale: string) => {
28
- const localePath =
29
- locale === defaultLocaleValue &&
30
- localeUrlStrategy !== LocaleUrlStrategy.ShowAllLocales
31
- ? ''
32
- : `/${locale}`;
28
+ const { protocol, hostname, port, search, pathname } = location;
29
+ const pathnameWithoutLocale = pathname.replace(urlLocaleMatcherRegex, '');
33
30
 
34
- const pathnameWithoutLocale = location.pathname.replace(
35
- urlLocaleMatcherRegex,
36
- ''
37
- );
31
+ let targetUrl;
32
+
33
+ if (localeUrlStrategy === LocaleUrlStrategy.Subdomain) {
34
+ const hostParts = hostname.split('.');
35
+ const subDomain = hostParts[0];
36
+ const isSubdomainLocale = locales.some((loc) => loc.value === subDomain);
37
+ const baseDomain = isSubdomainLocale
38
+ ? hostParts.slice(1).join('.')
39
+ : hostname;
40
+
41
+ const formattedPort = port ? `:${port}` : '';
42
+
43
+ targetUrl = `${protocol}//${locale}.${baseDomain}${formattedPort}${pathnameWithoutLocale}${search}`;
44
+ } else {
45
+ const shouldHideLocale =
46
+ locale === defaultLocaleValue &&
47
+ localeUrlStrategy !== LocaleUrlStrategy.ShowAllLocales;
48
+ const localePath = shouldHideLocale ? '' : `/${locale}`;
49
+
50
+ targetUrl = `${localePath}${pathnameWithoutLocale}${search}`;
51
+ }
38
52
 
39
53
  // router.push is not used because reloading the page also clears the client side cache.
40
- location.href = `${localePath}${pathnameWithoutLocale}${location.search}`;
54
+ location.href = targetUrl;
41
55
  };
42
56
 
43
57
  /**
@@ -27,8 +27,11 @@ export const useRouter = () => {
27
27
  );
28
28
 
29
29
  url.pathname = `${
30
- locale === defaultLocale?.value &&
31
- localeUrlStrategy === LocaleUrlStrategy.HideDefaultLocale
30
+ localeUrlStrategy === LocaleUrlStrategy.Subdomain ||
31
+ (locale === defaultLocale?.value &&
32
+ [LocaleUrlStrategy.HideDefaultLocale].includes(
33
+ localeUrlStrategy as LocaleUrlStrategy
34
+ ))
32
35
  ? ''
33
36
  : `/${locale}`
34
37
  }${pathnameWithoutLocale}`;
@@ -0,0 +1,24 @@
1
+ import { useEffect } from 'react';
2
+ import * as Sentry from '@sentry/nextjs';
3
+ import { ClientLogType } from '@akinon/next/sentry';
4
+
5
+ export const useSentryUncaughtErrors = (error: Error & { digest?: string }) => {
6
+ useEffect(() => {
7
+ Sentry.withScope(function (scope) {
8
+ scope.setLevel('fatal');
9
+ scope.setTags({
10
+ APP_TYPE: 'ProjectZeroNext',
11
+ TYPE: 'Client',
12
+ LOG_TYPE: ClientLogType.UNCAUGHT_ERROR_PAGE
13
+ });
14
+ scope.setExtra('error', error);
15
+
16
+ const error_ = new Error('FATAL: Uncaught client error');
17
+ error_.name = 'UNCAUGHT_ERROR_PAGE';
18
+
19
+ Sentry.captureException(error_, {
20
+ fingerprint: ['UNCAUGHT_ERROR_PAGE', error.digest]
21
+ });
22
+ });
23
+ }, [error]);
24
+ };
@@ -1,5 +1,15 @@
1
+ import { initSentry } from '../sentry';
2
+ import * as Sentry from '@sentry/nextjs';
3
+
1
4
  export async function register() {
2
5
  if (process.env.NEXT_RUNTIME === 'nodejs') {
3
6
  await import('./node');
7
+ initSentry('Server');
8
+ }
9
+
10
+ if (process.env.NEXT_RUNTIME === 'edge') {
11
+ initSentry('Edge');
4
12
  }
5
13
  }
14
+
15
+ export const onRequestError = Sentry.captureRequestError;
package/jest.config.js ADDED
@@ -0,0 +1,19 @@
1
+ const path = require('path');
2
+
3
+ module.exports = {
4
+ preset: 'ts-jest',
5
+ testEnvironment: 'node',
6
+ rootDir: path.resolve(__dirname),
7
+ roots: [],
8
+ testMatch: ['**/*.test.ts'],
9
+ testPathIgnorePatterns: [],
10
+ transformIgnorePatterns: [],
11
+ transform: {
12
+ '^.+\\.(tsx?|jsx?|mjs?)$': [
13
+ 'ts-jest',
14
+ {
15
+ tsconfig: path.resolve(__dirname, '__tests__/tsconfig.json')
16
+ }
17
+ ]
18
+ }
19
+ };
@@ -23,10 +23,10 @@ CacheHandler.onCreation(async () => {
23
23
  timeoutMs: 5000
24
24
  });
25
25
 
26
- const localHandler = createLruHandler();
26
+ // const localHandler = createLruHandler();
27
27
 
28
28
  return {
29
- handlers: [redisHandler, localHandler]
29
+ handlers: [redisHandler]
30
30
  };
31
31
  });
32
32
 
package/lib/cache.ts CHANGED
@@ -156,10 +156,6 @@ export class Cache {
156
156
  handler: () => Promise<T>,
157
157
  options?: CacheOptions
158
158
  ): Promise<T> {
159
- if (Settings.usePrettyUrlRoute) {
160
- return await handler();
161
- }
162
-
163
159
  const requiredVariables = [
164
160
  process.env.CACHE_HOST,
165
161
  process.env.CACHE_PORT,
@@ -178,6 +174,10 @@ export class Cache {
178
174
  const _options = Object.assign(defaultOptions, options);
179
175
  const formattedKey = Cache.formatKey(key, locale);
180
176
 
177
+ if (Settings.usePrettyUrlRoute) {
178
+ _options.expire = 120;
179
+ }
180
+
181
181
  logger.debug('Cache wrap', { key, formattedKey, _options });
182
182
 
183
183
  if (_options.cache) {
@@ -1,5 +1,6 @@
1
1
  export enum LocaleUrlStrategy {
2
2
  HideAllLocales = 'hide-all-locales',
3
3
  HideDefaultLocale = 'hide-default-locale',
4
- ShowAllLocales = 'show-all-locales'
4
+ ShowAllLocales = 'show-all-locales',
5
+ Subdomain = 'subdomain'
5
6
  }
@@ -19,6 +19,8 @@ import withLocale from './locale';
19
19
  import logger from '../utils/log';
20
20
  import { user } from '../data/urls';
21
21
  import { getUrlPathWithLocale } from '../utils/localization';
22
+ import getRootHostname from '../utils/get-root-hostname';
23
+ import { LocaleUrlStrategy } from '../localization';
22
24
 
23
25
  const withPzDefault =
24
26
  (middleware: NextMiddleware) =>
@@ -95,7 +97,9 @@ const withPzDefault =
95
97
 
96
98
  if (
97
99
  req.nextUrl.pathname.includes('/orders/hooks/') ||
98
- req.nextUrl.pathname.includes('/orders/checkout-with-token/')
100
+ req.nextUrl.pathname.includes('/orders/checkout-with-token/') ||
101
+ req.nextUrl.pathname.includes('/hooks/cash_register/complete/') ||
102
+ req.nextUrl.pathname.includes('/hooks/cash_register/pre_order/')
99
103
  ) {
100
104
  const queryString = searchParams.toString();
101
105
  const currency = searchParams.get('currency')?.toLowerCase();
@@ -285,14 +289,16 @@ const withPzDefault =
285
289
  url.pathname =
286
290
  url.pathname +
287
291
  (/\/$/.test(url.pathname) ? '' : '/') +
288
- `searchparams|${url.searchParams.toString()}`;
292
+ `searchparams|${encodeURIComponent(
293
+ url.searchParams.toString()
294
+ )}`;
289
295
  }
290
296
 
291
297
  if (
292
298
  !req.middlewareParams.found &&
293
299
  Settings.customNotFoundEnabled
294
300
  ) {
295
- let pathname = url.pathname
301
+ const pathname = url.pathname
296
302
  .replace(/\/+$/, '')
297
303
  .split('/');
298
304
  url.pathname = url.pathname.replace(
@@ -337,6 +343,21 @@ const withPzDefault =
337
343
  middlewareResult = NextResponse.rewrite(url);
338
344
  }
339
345
 
346
+ const { localeUrlStrategy } =
347
+ Settings.localization;
348
+
349
+ const fallbackHost =
350
+ req.headers.get('x-forwarded-host') ||
351
+ req.headers.get('host');
352
+ const hostname =
353
+ process.env.NEXT_PUBLIC_URL ||
354
+ `https://${fallbackHost}`;
355
+ const rootHostname =
356
+ localeUrlStrategy ===
357
+ LocaleUrlStrategy.Subdomain
358
+ ? getRootHostname(hostname)
359
+ : null;
360
+
340
361
  if (
341
362
  !url.pathname.startsWith(`/${currency}/orders`)
342
363
  ) {
@@ -346,6 +367,7 @@ const withPzDefault =
346
367
  ? locale
347
368
  : defaultLocaleValue,
348
369
  {
370
+ domain: rootHostname,
349
371
  sameSite: 'none',
350
372
  secure: true,
351
373
  expires: new Date(
@@ -354,10 +376,12 @@ const withPzDefault =
354
376
  }
355
377
  );
356
378
  }
379
+
357
380
  middlewareResult.cookies.set(
358
381
  'pz-currency',
359
382
  currency,
360
383
  {
384
+ domain: rootHostname,
361
385
  sameSite: 'none',
362
386
  secure: true,
363
387
  expires: new Date(
@@ -406,7 +430,10 @@ const withPzDefault =
406
430
  ).json();
407
431
  middlewareResult.cookies.set(
408
432
  'csrftoken',
409
- csrf_token
433
+ csrf_token,
434
+ {
435
+ domain: rootHostname
436
+ }
410
437
  );
411
438
  }
412
439
  } catch (error) {
@@ -4,17 +4,33 @@ import { PzNextRequest } from '.';
4
4
  import { LocaleUrlStrategy } from '../localization';
5
5
  import { urlLocaleMatcherRegex } from '../utils';
6
6
  import logger from '../utils/log';
7
+ import { getUrlPathWithLocale } from '../utils/localization';
7
8
 
8
- const getMatchedLocale = (pathname: string) => {
9
+ const getMatchedLocale = (pathname: string, req: PzNextRequest) => {
9
10
  let matchedLocale = pathname.match(urlLocaleMatcherRegex)?.[0] ?? '';
10
11
  matchedLocale = matchedLocale.replace('/', '');
11
12
 
13
+ const { localeUrlStrategy, defaultLocaleValue } = settings.localization;
14
+
15
+ if (localeUrlStrategy === LocaleUrlStrategy.Subdomain) {
16
+ const host =
17
+ req.headers.get('x-forwarded-host') || req.headers.get('host') || '';
18
+
19
+ if (host) {
20
+ const subDomain = host.split('.')[0] || '';
21
+ const subDomainLocaleMatched = `/${subDomain}`.match(
22
+ urlLocaleMatcherRegex
23
+ );
24
+
25
+ if (subDomainLocaleMatched && subDomainLocaleMatched[0]) {
26
+ matchedLocale = subDomainLocaleMatched[0].slice(1);
27
+ }
28
+ }
29
+ }
30
+
12
31
  if (!matchedLocale.length) {
13
- if (
14
- settings.localization.localeUrlStrategy !==
15
- LocaleUrlStrategy.ShowAllLocales
16
- ) {
17
- matchedLocale = settings.localization.defaultLocaleValue;
32
+ if (localeUrlStrategy !== LocaleUrlStrategy.ShowAllLocales) {
33
+ matchedLocale = defaultLocaleValue;
18
34
  }
19
35
  }
20
36
 
@@ -28,8 +44,10 @@ const withLocale =
28
44
 
29
45
  try {
30
46
  const url = req.nextUrl.clone();
31
- const matchedLocale = getMatchedLocale(url.pathname);
32
- let { localeUrlStrategy, defaultLocaleValue, redirectToDefaultLocale } =
47
+ const matchedLocale = getMatchedLocale(url.pathname, req);
48
+ let { localeUrlStrategy } = settings.localization;
49
+
50
+ const { defaultLocaleValue, redirectToDefaultLocale } =
33
51
  settings.localization;
34
52
 
35
53
  localeUrlStrategy =
@@ -50,8 +68,15 @@ const withLocale =
50
68
  redirectToDefaultLocale &&
51
69
  req.method === 'GET'
52
70
  ) {
53
- url.pathname = `/${defaultLocaleValue}${url.pathname}`;
54
- return NextResponse.redirect(url);
71
+ // Redirect to existing or default locale
72
+
73
+ url.pathname = getUrlPathWithLocale(
74
+ url.pathname,
75
+ req.cookies.get('pz-locale')?.value
76
+ );
77
+
78
+ // Use 303 for POST requests
79
+ return NextResponse.redirect(url, 303);
55
80
  }
56
81
 
57
82
  req.middlewareParams.rewrites.locale = matchedLocale;
@@ -61,7 +86,6 @@ const withLocale =
61
86
  ip
62
87
  });
63
88
  }
64
-
65
89
  return middleware(req, event);
66
90
  };
67
91
 
@@ -60,6 +60,22 @@ const withUrlRedirection =
60
60
 
61
61
  const setCookies = request.headers.getSetCookie();
62
62
 
63
+ if (settings.commerceRedirectionIgnoreList) {
64
+ const shouldIgnoreRedirect =
65
+ settings.commerceRedirectionIgnoreList.some((ignorePath) =>
66
+ redirectUrl.pathname.startsWith(
67
+ getUrlPathWithLocale(
68
+ ignorePath,
69
+ req.middlewareParams.rewrites.locale
70
+ )
71
+ )
72
+ );
73
+
74
+ if (shouldIgnoreRedirect) {
75
+ return middleware(req, event);
76
+ }
77
+ }
78
+
63
79
  const response = NextResponse.redirect(redirectUrl.toString(), {
64
80
  status: request.status
65
81
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@akinon/next",
3
3
  "description": "Core package for Project Zero Next",
4
- "version": "2.0.0-beta.1",
4
+ "version": "2.0.0-beta.11",
5
5
  "private": false,
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -13,14 +13,18 @@
13
13
  "pz-predev": "bin/pz-predev.js",
14
14
  "pz-postdev": "bin/pz-postdev.js"
15
15
  },
16
+ "scripts": {
17
+ "test": "jest"
18
+ },
16
19
  "dependencies": {
20
+ "@neshca/cache-handler": "1.5.1",
17
21
  "@opentelemetry/exporter-trace-otlp-http": "0.46.0",
18
22
  "@opentelemetry/resources": "1.19.0",
19
23
  "@opentelemetry/sdk-node": "0.46.0",
20
24
  "@opentelemetry/sdk-trace-node": "1.19.0",
21
25
  "@opentelemetry/semantic-conventions": "1.19.0",
22
26
  "@reduxjs/toolkit": "1.9.7",
23
- "@neshca/cache-handler": "1.5.1",
27
+ "@sentry/nextjs": "9.5.0",
24
28
  "cross-spawn": "7.0.3",
25
29
  "generic-pool": "3.9.0",
26
30
  "react-redux": "8.1.3",
@@ -30,13 +34,13 @@
30
34
  "set-cookie-parser": "2.6.0"
31
35
  },
32
36
  "devDependencies": {
33
- "@akinon/eslint-plugin-projectzero": "2.0.0-beta.1",
37
+ "@akinon/eslint-plugin-projectzero": "2.0.0-beta.11",
34
38
  "@types/react-redux": "7.1.30",
35
39
  "@types/set-cookie-parser": "2.4.7",
36
40
  "@typescript-eslint/eslint-plugin": "8.18.2",
37
41
  "@typescript-eslint/parser": "8.18.2",
38
42
  "eslint": "9.17.0",
39
- "eslint-config-next": "15.1.2",
43
+ "eslint-config-next": "15.3.2",
40
44
  "eslint-config-prettier": "9.1.0"
41
45
  }
42
46
  }
package/plugins.js CHANGED
@@ -14,5 +14,6 @@ module.exports = [
14
14
  'pz-multi-basket',
15
15
  'pz-saved-card',
16
16
  'pz-tabby-extension',
17
- 'pz-apple-pay'
17
+ 'pz-apple-pay',
18
+ 'pz-tamara-extension'
18
19
  ];
@@ -16,12 +16,11 @@ import {
16
16
  setLoyaltyBalance,
17
17
  setPaymentChoices,
18
18
  setPaymentOptions,
19
- setPreOrder,
20
19
  setRetailStores,
21
20
  setShippingOptions,
22
- setShippingStepCompleted,
23
21
  setHepsipayAvailability,
24
- setWalletPaymentData
22
+ setWalletPaymentData,
23
+ setPayOnDeliveryOtpModalActive
25
24
  } from '../../redux/reducers/checkout';
26
25
  import { RootState, TypedDispatch } from 'redux/store';
27
26
  import { checkoutApi } from '../../data/client/checkout';
@@ -37,6 +36,7 @@ interface CheckoutResult {
37
36
  errors?: Record<string, string[]>;
38
37
  pre_order?: PreOrder;
39
38
  context_list?: CheckoutContext[];
39
+ redirect_url?: string;
40
40
  };
41
41
  }
42
42
 
@@ -58,142 +58,29 @@ export const errorMiddleware: Middleware = ({ dispatch }: MiddlewareParams) => {
58
58
  };
59
59
  };
60
60
 
61
- export const preOrderMiddleware: Middleware = ({
62
- getState,
63
- dispatch
64
- }: MiddlewareParams) => {
61
+ export const redirectUrlMiddleware: Middleware = () => {
65
62
  return (next) => (action) => {
66
63
  const result: CheckoutResult = next(action);
67
- const preOrder = result?.payload?.pre_order;
64
+ const redirectUrl = result?.payload?.redirect_url;
68
65
 
69
- if (
70
- !preOrder ||
71
- action?.meta?.arg?.endpointName === 'guestLogin' ||
72
- action?.meta?.arg?.endpointName === 'getCheckoutLoyaltyBalance'
73
- ) {
74
- return result;
75
- }
66
+ if (redirectUrl) {
67
+ const currentLocale = getCookie('pz-locale');
76
68
 
77
- const {
78
- deliveryOptions,
79
- addressList: addresses,
80
- shippingOptions,
81
- dataSourceShippingOptions,
82
- paymentOptions,
83
- installmentOptions,
84
- attributeBasedShippingOptions
85
- } = getState().checkout;
86
- const { endpoints: apiEndpoints } = checkoutApi;
69
+ let url = redirectUrl;
87
70
 
88
- if (preOrder.is_redirected) {
89
- const contextList = result?.payload?.context_list;
71
+ if (currentLocale && !redirectUrl.includes('/orders/redirection')) {
72
+ const { defaultLocaleValue, localeUrlStrategy } = settings.localization;
90
73
 
91
- if (
92
- contextList.find(
93
- (ctx) => ctx.page_name === 'RedirectionPaymentSelectedPage'
94
- )
95
- ) {
96
- dispatch(
97
- apiEndpoints.setPaymentOption.initiate(preOrder.payment_option?.pk)
98
- );
99
- return;
74
+ url =
75
+ currentLocale === defaultLocaleValue &&
76
+ localeUrlStrategy !== LocaleUrlStrategy.ShowAllLocales
77
+ ? redirectUrl
78
+ : `/${currentLocale}${redirectUrl}`;
100
79
  }
101
- }
102
-
103
- dispatch(setPreOrder(preOrder));
104
80
 
105
- if (!preOrder.delivery_option && deliveryOptions.length > 0) {
106
- dispatch(
107
- apiEndpoints.setDeliveryOption.initiate(
108
- deliveryOptions.find((opt) => opt.delivery_option_type === 'customer')
109
- ?.pk
110
- )
111
- );
112
- }
113
-
114
- if (
115
- (!preOrder.shipping_address || !preOrder.billing_address) &&
116
- addresses.length > 0 &&
117
- (!preOrder.delivery_option ||
118
- preOrder.delivery_option.delivery_option_type === 'customer')
119
- ) {
120
- dispatch(
121
- apiEndpoints.setAddresses.initiate({
122
- shippingAddressPk: addresses[0].pk,
123
- billingAddressPk: addresses[0].pk
124
- })
125
- );
126
- }
127
-
128
- if (
129
- shippingOptions.length > 0 &&
130
- (!preOrder.shipping_option ||
131
- !shippingOptions.find((opt) => opt.pk === preOrder.shipping_option?.pk))
132
- ) {
133
- dispatch(apiEndpoints.setShippingOption.initiate(shippingOptions[0].pk));
81
+ window.location.href = url;
134
82
  }
135
83
 
136
- if (
137
- dataSourceShippingOptions.length > 0 &&
138
- !preOrder.data_source_shipping_options
139
- ) {
140
- const selectedDataSourceShippingOptionsPks =
141
- dataSourceShippingOptions.map(
142
- (opt) => opt.data_source_shipping_options[0].pk
143
- );
144
-
145
- dispatch(
146
- apiEndpoints.setDataSourceShippingOptions.initiate(
147
- selectedDataSourceShippingOptionsPks
148
- )
149
- );
150
- }
151
-
152
- if (
153
- Object.keys(attributeBasedShippingOptions).length > 0 &&
154
- !preOrder.attribute_based_shipping_options
155
- ) {
156
- const initialSelectedOptions: Record<string, number> = Object.fromEntries(
157
- Object.entries(attributeBasedShippingOptions).map(([key, options]) => [
158
- key,
159
- options.attribute_based_shipping_options[0].pk
160
- ])
161
- );
162
-
163
- dispatch(
164
- apiEndpoints.setAttributeBasedShippingOptions.initiate(
165
- initialSelectedOptions
166
- )
167
- );
168
- }
169
-
170
- if (!preOrder.payment_option && paymentOptions.length > 0) {
171
- dispatch(apiEndpoints.setPaymentOption.initiate(paymentOptions[0].pk));
172
- }
173
-
174
- if (
175
- !preOrder.installment &&
176
- preOrder.payment_option?.payment_type !== 'saved_card' &&
177
- installmentOptions.length > 0
178
- ) {
179
- dispatch(
180
- apiEndpoints.setInstallmentOption.initiate(installmentOptions[0].pk)
181
- );
182
- }
183
-
184
- dispatch(
185
- setShippingStepCompleted(
186
- [
187
- preOrder.delivery_option?.delivery_option_type === 'retail_store'
188
- ? true
189
- : preOrder.shipping_address?.pk,
190
- preOrder.billing_address?.pk,
191
- preOrder.shipping_option?.pk,
192
- addresses.length > 0
193
- ].every(Boolean)
194
- )
195
- );
196
-
197
84
  return result;
198
85
  };
199
86
  };
@@ -203,8 +90,9 @@ export const contextListMiddleware: Middleware = ({
203
90
  getState
204
91
  }: MiddlewareParams) => {
205
92
  return (next) => (action) => {
206
- const { isMobileApp } = getState().root;
93
+ const { isMobileApp, userPhoneNumber } = getState().root;
207
94
  const result: CheckoutResult = next(action);
95
+ const preOrder = result?.payload?.pre_order;
208
96
 
209
97
  if (result?.payload?.context_list) {
210
98
  result.payload.context_list.forEach((context) => {
@@ -323,6 +211,18 @@ export const contextListMiddleware: Middleware = ({
323
211
  if (context.page_context.retail_stores) {
324
212
  dispatch(setRetailStores(context.page_context.retail_stores));
325
213
  }
214
+
215
+ if (context.page_name === 'SendSmsPage' && !preOrder?.phone_number) {
216
+ dispatch(
217
+ checkoutApi.endpoints.sendSms.initiate({
218
+ phone_number: userPhoneNumber ?? preOrder?.user_phone_number
219
+ })
220
+ );
221
+ }
222
+
223
+ if (context.page_name === 'VerifySmsPage') {
224
+ dispatch(setPayOnDeliveryOtpModalActive(true));
225
+ }
326
226
  });
327
227
  }
328
228