@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
@@ -1,6 +1,6 @@
1
1
  import { Cache, CacheKey } from '../../lib/cache';
2
2
  import { category } from '../urls';
3
- import { GetCategoryResponse } from '../../types';
3
+ import { GetCategoryResponse, SearchParams } from '../../types';
4
4
  import { generateCommerceSearchParams } from '../../utils';
5
5
  import appFetch from '../../utils/app-fetch';
6
6
  import { ServerVariables } from '../../utils/server-variables';
@@ -9,7 +9,7 @@ const getSpecialPageDataHandler = (
9
9
  pk: number,
10
10
  locale: string,
11
11
  currency: string,
12
- searchParams: URLSearchParams,
12
+ searchParams: SearchParams,
13
13
  headers?: Record<string, string>
14
14
  ) => {
15
15
  return async function () {
@@ -42,7 +42,7 @@ export const getSpecialPageData = async ({
42
42
  pk: number;
43
43
  locale?: string;
44
44
  currency?: string;
45
- searchParams: URLSearchParams;
45
+ searchParams: SearchParams;
46
46
  headers?: Record<string, string>;
47
47
  }) => {
48
48
  return Cache.wrap(
@@ -50,7 +50,8 @@ export const getSpecialPageData = async ({
50
50
  locale,
51
51
  getSpecialPageDataHandler(pk, locale, currency, searchParams, headers),
52
52
  {
53
- expire: 300
53
+ expire: 300,
54
+ compressed: true
54
55
  }
55
56
  );
56
57
  };
@@ -1,34 +1,119 @@
1
1
  import { Cache, CacheKey } from '../../lib/cache';
2
2
  import 'server-only';
3
- import { CacheOptions, WidgetResultType } from '../../types';
3
+ import { CacheOptions, WidgetResultType, WidgetSchemaType } from '../../types';
4
4
  import appFetch from '../../utils/app-fetch';
5
5
  import { widgets } from '../urls';
6
6
  import { ServerVariables } from '../../utils/server-variables';
7
7
 
8
8
  const getWidgetDataHandler =
9
- (slug: string, locale: string, currency: string) => async () => {
9
+ (
10
+ slug: string,
11
+ locale: string,
12
+ currency: string,
13
+ headers?: Record<string, string>
14
+ ) =>
15
+ async () => {
10
16
  if (!slug) {
11
17
  return null;
12
18
  }
13
19
 
14
- return await appFetch({ url: widgets.getWidget(slug), locale, currency });
20
+ return await appFetch({
21
+ url: widgets.getWidget(slug),
22
+ locale,
23
+ currency,
24
+ init: {
25
+ headers
26
+ }
27
+ });
28
+ };
29
+
30
+ const getWidgetSchemaDataHandler =
31
+ (widgetSlug: string, locale: string, currency: string) => async () => {
32
+ if (!widgetSlug) {
33
+ return null;
34
+ }
35
+
36
+ return await appFetch({
37
+ url: widgets.getWidgetSchema(widgetSlug),
38
+ locale,
39
+ currency
40
+ });
15
41
  };
16
42
 
17
43
  export const getWidgetData = async <T>({
18
44
  slug,
19
45
  locale = ServerVariables.locale,
20
46
  currency = ServerVariables.currency,
21
- cacheOptions
47
+ cacheOptions,
48
+ headers
22
49
  }: {
23
50
  slug: string;
24
51
  locale?: string;
25
52
  currency?: string;
26
53
  cacheOptions?: CacheOptions;
54
+ headers?: Record<string, string>;
27
55
  }): Promise<WidgetResultType<T>> => {
28
56
  return Cache.wrap(
29
57
  CacheKey.Widget(slug),
30
58
  locale,
31
- getWidgetDataHandler(slug, locale, currency),
59
+ getWidgetDataHandler(slug, locale, currency, headers),
60
+ {
61
+ compressed: true,
62
+ ...cacheOptions
63
+ }
64
+ );
65
+ };
66
+
67
+ const getCollectionWidgetDataHandler =
68
+ (slug: string, locale: string, currency: string) => async () => {
69
+ if (!slug) {
70
+ return null;
71
+ }
72
+
73
+ return await appFetch({
74
+ url: widgets.getCollectionWidget(slug),
75
+ locale,
76
+ currency
77
+ });
78
+ };
79
+
80
+ export const getCollectionWidgetData = async <T>({
81
+ slug,
82
+ locale = ServerVariables.locale,
83
+ currency = ServerVariables.currency,
84
+ cacheOptions
85
+ }: {
86
+ slug: string;
87
+ locale?: string;
88
+ currency?: string;
89
+ cacheOptions?: CacheOptions;
90
+ }): Promise<WidgetResultType<T>> => {
91
+ return Cache.wrap(
92
+ CacheKey.Widget(`collection:${slug}`),
93
+ locale,
94
+ getCollectionWidgetDataHandler(slug, locale, currency),
95
+ {
96
+ compressed: true,
97
+ ...cacheOptions
98
+ }
99
+ );
100
+ };
101
+
102
+ export const getWidgetSchemaData = async <T>({
103
+ widgetSlug,
104
+ locale = ServerVariables.locale,
105
+ currency = ServerVariables.currency,
106
+ cacheOptions
107
+ }: {
108
+ widgetSlug: string;
109
+ locale?: string;
110
+ currency?: string;
111
+ cacheOptions?: CacheOptions;
112
+ }): Promise<WidgetSchemaType<T>> => {
113
+ return Cache.wrap(
114
+ CacheKey.WidgetSchema(widgetSlug),
115
+ locale,
116
+ getWidgetSchemaDataHandler(widgetSlug, locale, currency),
32
117
  cacheOptions
33
118
  );
34
119
  };
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,10 @@ 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',
146
+ saveSampleProducts: '/orders/checkout/?page=SampleProductPage'
141
147
  };
142
148
 
143
149
  export const flatpage = {
@@ -160,7 +166,8 @@ export const misc = {
160
166
  parent ? `&parent=${parent}` : ''
161
167
  }`,
162
168
  cmsSeo: (slug: string | string[]) => `/cms/seo/?url=${slug ? slug : '/'}`,
163
- setCurrency: '/users/activate-currency/'
169
+ setCurrency: '/users/activate-currency/',
170
+ bukalemunImageUrl: (params: string) => `/bukalemun/?${params}`
164
171
  };
165
172
 
166
173
  export const product = {
@@ -174,7 +181,9 @@ export const product = {
174
181
  slug: (slug: string) => `/${slug}/`,
175
182
  categoryUrl: (pk: number) => `/products/${pk}/category_nodes/?limit=1`,
176
183
  breadcrumbUrl: (menuitemmodel: string) =>
177
- `/menus/generate_breadcrumb/?item=${menuitemmodel}&generator_name=menu_item`
184
+ `/menus/generate_breadcrumb/?item=${menuitemmodel}&generator_name=menu_item`,
185
+ bundleProduct: (productPk: string, queryString: string) =>
186
+ `/bundle-product/${productPk}/?${queryString}`
178
187
  };
179
188
 
180
189
  export const wishlist = {
@@ -234,11 +243,13 @@ export const b2b = {
234
243
  };
235
244
 
236
245
  export const widgets = {
237
- getWidget: (slug: string) => `/widgets/${slug}/`
246
+ getWidget: (slug: string) => `/widgets/${slug}/`,
247
+ getWidgetSchema: (widgetSlug: string) => `/widgets/${widgetSlug}/schema/`,
248
+ getCollectionWidget: (slug: string) => `/product_collection_widgets/${slug}/`
238
249
  };
239
250
 
240
251
  export const form = {
241
- getForm: (pk: number) => `/forms/${pk}/generate/`
252
+ getForm: (pk: number) => `/forms/${pk}/generate`
242
253
  };
243
254
 
244
255
  const URLS = {
@@ -1,4 +1,4 @@
1
- import { LayoutProps, PageProps, RootLayoutProps } from '../../types';
1
+ import { ResolvedLayoutProps, ResolvedPageProps, ResolvedRootLayoutProps } from '../../types';
2
2
  import React, { JSX } from 'react';
3
3
 
4
4
  type SegmentType = 'client-root' | 'layout' | 'page';
@@ -8,7 +8,7 @@ interface SegmentDefaultsOptions {
8
8
  }
9
9
 
10
10
  export const withSegmentDefaults =
11
- <T extends PageProps | LayoutProps | RootLayoutProps>(
11
+ <T extends ResolvedPageProps | ResolvedLayoutProps | ResolvedRootLayoutProps>(
12
12
  Component: (
13
13
  props?: T
14
14
  ) => null | JSX.Element | Promise<JSX.Element> | Promise<JSX.Element[]>,
@@ -1,10 +1,20 @@
1
- import settings from 'settings';
2
1
  import { JSX } from 'react';
3
- import { LayoutProps, PageProps, RootLayoutProps } from '../../types';
2
+ import settings from 'settings';
3
+ import {
4
+ ResolvedPageProps,
5
+ ResolvedLayoutProps,
6
+ ResolvedRootLayoutProps
7
+ } from '../../types';
4
8
  import { redirect } from 'next/navigation';
5
9
  import { ServerVariables } from '../../utils/server-variables';
6
10
  import { ROUTES } from 'routes';
7
11
  import logger from '../../utils/log';
12
+ import {
13
+ decodePzValue,
14
+ getPzSegmentsConfig,
15
+ getBuiltInSegments,
16
+ isLegacyMode
17
+ } from '../../utils/pz-segments';
8
18
 
9
19
  type SegmentType = 'root-layout' | 'layout' | 'page';
10
20
 
@@ -13,27 +23,50 @@ interface SegmentDefaultsOptions {
13
23
  }
14
24
 
15
25
  export const withSegmentDefaults =
16
- <T extends PageProps | LayoutProps | RootLayoutProps>(
26
+ <T extends ResolvedPageProps | ResolvedLayoutProps | ResolvedRootLayoutProps>(
17
27
  Component: (
18
28
  props?: T
19
29
  ) => null | JSX.Element | Promise<JSX.Element> | Promise<JSX.Element[]>,
20
30
  options: SegmentDefaultsOptions
21
31
  ) =>
22
- async (props: T) => {
23
- let componentProps = { ...props };
24
-
25
- const { locale, currency } = await props.params;
32
+ async (props: any) => {
33
+ const resolvedParams = await props.params;
34
+ const resolvedSearchParams = props.searchParams
35
+ ? await props.searchParams
36
+ : undefined;
37
+
38
+ let componentProps = {
39
+ ...props,
40
+ params: resolvedParams,
41
+ searchParams: resolvedSearchParams,
42
+ ...('children' in props ? { children: (props as any).children } : {})
43
+ } as T;
44
+
45
+ let localeValue: string;
46
+ let currencyValue: string;
47
+
48
+ if (isLegacyMode(settings)) {
49
+ localeValue = resolvedParams.locale;
50
+ currencyValue = resolvedParams.currency;
51
+ } else {
52
+ const pzConfig = getPzSegmentsConfig(settings);
53
+ const parsed = decodePzValue(resolvedParams.pz, pzConfig);
54
+ const builtIn = getBuiltInSegments(parsed, settings);
55
+ localeValue = builtIn.locale;
56
+ currencyValue = builtIn.currency;
57
+ }
26
58
 
27
59
  if (options.segmentType === 'root-layout') {
28
60
  componentProps = (await addRootLayoutProps(
29
- componentProps as RootLayoutProps
61
+ componentProps as ResolvedRootLayoutProps,
62
+ localeValue
30
63
  )) as T;
31
64
 
32
65
  checkRedisVariables();
33
66
  }
34
67
 
35
- ServerVariables.locale = await locale;
36
- ServerVariables.currency = await currency;
68
+ ServerVariables.locale = localeValue;
69
+ ServerVariables.currency = currencyValue;
37
70
 
38
71
  return await (
39
72
  <>
@@ -42,12 +75,21 @@ export const withSegmentDefaults =
42
75
  );
43
76
  };
44
77
 
45
- const addRootLayoutProps = async (componentProps: RootLayoutProps) => {
46
- const params = await componentProps.params;
47
-
48
- if (
49
- params.commerce !== encodeURIComponent(decodeURI(settings.commerceUrl)) ||
50
- !settings.localization.locales.find((l) => l.value === params.locale)
78
+ const addRootLayoutProps = async (
79
+ componentProps: ResolvedRootLayoutProps,
80
+ localeValue: string
81
+ ) => {
82
+ if (isLegacyMode(settings)) {
83
+ const params = componentProps.params;
84
+ if (
85
+ params.commerce !==
86
+ encodeURIComponent(decodeURI(settings.commerceUrl)) ||
87
+ !settings.localization.locales.find((l) => l.value === localeValue)
88
+ ) {
89
+ return redirect(ROUTES.HOME);
90
+ }
91
+ } else if (
92
+ !settings.localization.locales.find((l) => l.value === localeValue)
51
93
  ) {
52
94
  return redirect(ROUTES.HOME);
53
95
  }
@@ -55,12 +97,12 @@ const addRootLayoutProps = async (componentProps: RootLayoutProps) => {
55
97
  const { getTranslations } = settings.useOptimizedTranslations
56
98
  ? require('translations')
57
99
  : require('../../utils/server-translation');
58
- const translations = await getTranslations(params.locale);
100
+ const translations = await getTranslations(localeValue);
59
101
 
60
102
  componentProps.translations = translations;
61
103
 
62
104
  const locale = settings.localization.locales.find(
63
- (l) => l.value === params.locale
105
+ (l) => l.value === localeValue
64
106
  );
65
107
  const [isoCode] = locale.value.split('-');
66
108
 
@@ -75,10 +117,13 @@ const addRootLayoutProps = async (componentProps: RootLayoutProps) => {
75
117
  const checkRedisVariables = () => {
76
118
  const requiredVariableValues = [
77
119
  process.env.CACHE_HOST,
78
- process.env.CACHE_PORT,
79
- process.env.CACHE_SECRET
120
+ process.env.CACHE_PORT
80
121
  ];
81
122
 
123
+ if (!settings.usePrettyUrlRoute) {
124
+ requiredVariableValues.push(process.env.CACHE_SECRET);
125
+ }
126
+
82
127
  if (
83
128
  !requiredVariableValues.every((v) => v) &&
84
129
  process.env.NODE_ENV === 'production'
package/hooks/index.ts CHANGED
@@ -10,3 +10,7 @@ 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-logger';
14
+ export * from './use-logger-context';
15
+ export * from './use-sentry-uncaught-errors';
16
+ export * from './use-pz-params';
@@ -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
  /**
@@ -0,0 +1,114 @@
1
+ 'use client';
2
+
3
+ import React, {
4
+ createContext,
5
+ useContext,
6
+ ReactNode,
7
+ useMemo,
8
+ useRef,
9
+ useEffect
10
+ } from 'react';
11
+ import { useLogger, LogEntry, LogLevel } from './use-logger';
12
+
13
+ const LOG_LEVELS: LogLevel[] = ['info', 'warn', 'error'];
14
+
15
+ interface LoggerContextType {
16
+ logs: LogEntry[];
17
+ isVisible: boolean;
18
+ toggleVisibility: () => void;
19
+ clearLogs: () => void;
20
+ info: (message: string, payload?: any) => string;
21
+ warn: (message: string, payload?: any) => string;
22
+ error: (message: string, payload?: any) => string;
23
+ isDevelopment: boolean;
24
+ hasError: boolean;
25
+ hasWarning: boolean;
26
+ }
27
+
28
+ const LoggerContext = createContext<LoggerContextType | undefined>(undefined);
29
+
30
+ let globalAddLogFunction:
31
+ | ((level: string, message: string, payload?: any) => string)
32
+ | null = null;
33
+
34
+ // temporary queue for logs generated before the logger is initialized
35
+ const pendingLogs: Array<{ level: string; message: string; payload?: any }> =
36
+ [];
37
+
38
+ const createLogFunction =
39
+ (level: LogLevel) => (message: string, payload?: any) => {
40
+ if (
41
+ typeof window !== 'undefined' &&
42
+ process.env.NODE_ENV === 'development'
43
+ ) {
44
+ try {
45
+ if (globalAddLogFunction) {
46
+ globalAddLogFunction(level, message, payload);
47
+ } else {
48
+ pendingLogs.push({ level, message, payload });
49
+ }
50
+ } catch (err) {
51
+ // prevent errors
52
+ }
53
+ }
54
+
55
+ return '';
56
+ };
57
+
58
+ const stableLogger = LOG_LEVELS.reduce((logger, level) => {
59
+ logger[level] = createLogFunction(level);
60
+
61
+ return logger;
62
+ }, {} as Record<LogLevel, (message: string, payload?: any) => string>);
63
+
64
+ export const LoggerProvider = ({ children }: { children: ReactNode }) => {
65
+ const loggerHook = useLogger();
66
+
67
+ const addLogRef = useRef<
68
+ (level: string, message: string, payload?: any) => string
69
+ >((level, message, payload) => {
70
+ if (LOG_LEVELS.includes(level as LogLevel)) {
71
+ return loggerHook[level as LogLevel](message, payload);
72
+ }
73
+ return '';
74
+ });
75
+
76
+ useEffect(() => {
77
+ globalAddLogFunction = addLogRef.current;
78
+
79
+ if (pendingLogs.length > 0) {
80
+ pendingLogs.forEach((log) => {
81
+ if (globalAddLogFunction) {
82
+ globalAddLogFunction(log.level, log.message, log.payload);
83
+ }
84
+ });
85
+
86
+ pendingLogs.length = 0;
87
+ }
88
+
89
+ return () => {
90
+ globalAddLogFunction = null;
91
+ };
92
+ }, []);
93
+
94
+ const contextValue = useMemo(
95
+ () => loggerHook,
96
+ [loggerHook.logs, loggerHook.isVisible, loggerHook.isDevelopment] // eslint-disable-line react-hooks/exhaustive-deps
97
+ );
98
+
99
+ return (
100
+ <LoggerContext.Provider value={contextValue}>
101
+ {children}
102
+ </LoggerContext.Provider>
103
+ );
104
+ };
105
+
106
+ export const useLoggerContext = () => {
107
+ const context = useContext(LoggerContext);
108
+ if (context === undefined) {
109
+ throw new Error('useLoggerContext must be used within a LoggerProvider');
110
+ }
111
+ return context;
112
+ };
113
+
114
+ export const devLogger = stableLogger;
@@ -0,0 +1,92 @@
1
+ import { useState, useCallback, useEffect, useRef, useMemo } from 'react';
2
+
3
+ export type LogLevel = 'info' | 'warn' | 'error';
4
+
5
+ export interface LogEntry {
6
+ id: string;
7
+ level: LogLevel;
8
+ message: string;
9
+ timestamp: Date;
10
+ payload?: any;
11
+ }
12
+
13
+ const LOG_LEVELS: LogEntry['level'][] = ['info', 'warn', 'error'];
14
+
15
+ export function useLogger() {
16
+ const [logs, setLogs] = useState<LogEntry[]>([]);
17
+ const [isVisible, setIsVisible] = useState(false);
18
+ const logsRef = useRef<LogEntry[]>([]);
19
+ const [isDevelopment, setIsDevelopment] = useState(false);
20
+
21
+ useEffect(() => {
22
+ setIsDevelopment(
23
+ process.env.NODE_ENV === 'development' ||
24
+ window.location.hostname === 'localhost' ||
25
+ window.location.hostname === '127.0.0.1'
26
+ );
27
+ }, []);
28
+
29
+ useEffect(() => {
30
+ logsRef.current = logs;
31
+ }, [logs]);
32
+
33
+ const addLog = useCallback(
34
+ (level: LogEntry['level'], message: string, payload?: any) => {
35
+ const newLog: LogEntry = {
36
+ id: Math.random().toString(36).substring(2, 9),
37
+ level,
38
+ message,
39
+ timestamp: new Date(),
40
+ payload
41
+ };
42
+
43
+ setLogs((prevLogs) => [newLog, ...prevLogs]);
44
+
45
+ return newLog.id;
46
+ },
47
+ []
48
+ );
49
+
50
+ const clearLogs = useCallback(() => {
51
+ setLogs([]);
52
+ }, []);
53
+
54
+ const toggleVisibility = useCallback(() => {
55
+ setIsVisible((prev) => !prev);
56
+ }, []);
57
+
58
+ const createLogMethod = useCallback(
59
+ (level: LogEntry['level']) => (message: string, payload?: any) =>
60
+ addLog(level, message, payload),
61
+ [addLog]
62
+ );
63
+
64
+ const logMethods = useMemo(
65
+ () =>
66
+ LOG_LEVELS.reduce((methods, level) => {
67
+ methods[level] = createLogMethod(level);
68
+ return methods;
69
+ }, {} as Record<LogEntry['level'], (message: string, payload?: any) => string>),
70
+ [createLogMethod]
71
+ );
72
+
73
+ const hasError = useMemo(
74
+ () => logs.some((log) => log.level === 'error'),
75
+ [logs]
76
+ );
77
+ const hasWarning = useMemo(
78
+ () => logs.some((log) => log.level === 'warn'),
79
+ [logs]
80
+ );
81
+
82
+ return {
83
+ logs,
84
+ isVisible,
85
+ toggleVisibility,
86
+ clearLogs,
87
+ ...logMethods,
88
+ isDevelopment,
89
+ hasError,
90
+ hasWarning
91
+ };
92
+ }
@@ -0,0 +1,21 @@
1
+ import { useAppSelector } from '../redux/hooks';
2
+
3
+ export const useLoyaltyAvailability = () => {
4
+ const { paymentOptions, unavailablePaymentOptions } = useAppSelector(
5
+ (state) => state.checkout
6
+ );
7
+
8
+ const hasLoyaltyInAvailable = paymentOptions.some(
9
+ (option) =>
10
+ option.payment_type === 'loyalty_money' ||
11
+ option.payment_type === 'loyalty'
12
+ );
13
+
14
+ const hasLoyaltyInUnavailable = unavailablePaymentOptions.some(
15
+ (option) =>
16
+ option.payment_type === 'loyalty_money' ||
17
+ option.payment_type === 'loyalty'
18
+ );
19
+
20
+ return hasLoyaltyInAvailable || hasLoyaltyInUnavailable;
21
+ };
@@ -21,7 +21,8 @@ export const usePaymentOptions = () => {
21
21
  credit_payment: 'pz-credit-payment',
22
22
  masterpass: 'pz-masterpass',
23
23
  saved_card: 'pz-saved-card',
24
- gpay: 'pz-gpay'
24
+ gpay: 'pz-gpay',
25
+ masterpass_rest: 'pz-masterpass-rest'
25
26
  };
26
27
 
27
28
  const isInitialTypeIncluded = (type: string) => initialTypes.has(type);