@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.
- package/.eslintrc.js +12 -0
- package/CHANGELOG.md +377 -7
- package/__tests__/next-config.test.ts +83 -0
- package/__tests__/tsconfig.json +23 -0
- package/api/auth.ts +133 -44
- package/api/barcode-search.ts +59 -0
- package/api/cache.ts +41 -5
- package/api/client.ts +21 -4
- package/api/form.ts +85 -0
- package/api/image-proxy.ts +75 -0
- package/api/product-categories.ts +53 -0
- package/api/similar-product-list.ts +63 -0
- package/api/similar-products.ts +111 -0
- package/api/virtual-try-on.ts +382 -0
- package/assets/styles/index.scss +84 -0
- package/babel.config.js +6 -0
- package/bin/pz-generate-routes.js +115 -0
- package/bin/pz-prebuild.js +1 -0
- package/bin/pz-predev.js +1 -0
- package/bin/pz-run-tests.js +99 -0
- package/bin/run-prebuild-tests.js +46 -0
- package/components/accordion.tsx +20 -5
- package/components/button.tsx +51 -36
- package/components/client-root.tsx +138 -2
- package/components/file-input.tsx +65 -3
- package/components/index.ts +1 -0
- package/components/input.tsx +1 -1
- package/components/link.tsx +46 -16
- package/components/logger-popup.tsx +213 -0
- package/components/modal.tsx +32 -16
- package/components/plugin-module.tsx +62 -3
- package/components/price.tsx +2 -2
- package/components/select.tsx +1 -1
- package/components/selected-payment-option-view.tsx +21 -0
- package/components/theme-editor/blocks/accordion-block.tsx +136 -0
- package/components/theme-editor/blocks/block-renderer-registry.tsx +77 -0
- package/components/theme-editor/blocks/button-block.tsx +593 -0
- package/components/theme-editor/blocks/counter-block.tsx +348 -0
- package/components/theme-editor/blocks/divider-block.tsx +20 -0
- package/components/theme-editor/blocks/embed-block.tsx +208 -0
- package/components/theme-editor/blocks/group-block.tsx +116 -0
- package/components/theme-editor/blocks/hotspot-block.tsx +147 -0
- package/components/theme-editor/blocks/icon-block.tsx +230 -0
- package/components/theme-editor/blocks/image-block.tsx +137 -0
- package/components/theme-editor/blocks/image-gallery-block.tsx +269 -0
- package/components/theme-editor/blocks/input-block.tsx +123 -0
- package/components/theme-editor/blocks/link-block.tsx +216 -0
- package/components/theme-editor/blocks/lottie-block.tsx +325 -0
- package/components/theme-editor/blocks/map-block.tsx +89 -0
- package/components/theme-editor/blocks/slider-block.tsx +595 -0
- package/components/theme-editor/blocks/tab-block.tsx +10 -0
- package/components/theme-editor/blocks/text-block.tsx +52 -0
- package/components/theme-editor/blocks/video-block.tsx +122 -0
- package/components/theme-editor/components/action-toolbar.tsx +305 -0
- package/components/theme-editor/components/designer-overlay.tsx +74 -0
- package/components/theme-editor/components/with-designer-features.tsx +142 -0
- package/components/theme-editor/dynamic-font-loader.tsx +79 -0
- package/components/theme-editor/hooks/use-designer-features.tsx +100 -0
- package/components/theme-editor/hooks/use-external-designer.tsx +95 -0
- package/components/theme-editor/hooks/use-native-widget-data.ts +188 -0
- package/components/theme-editor/hooks/use-visibility-context.ts +27 -0
- package/components/theme-editor/placeholder-registry.ts +31 -0
- package/components/theme-editor/sections/before-after-section.tsx +245 -0
- package/components/theme-editor/sections/contact-form-section.tsx +563 -0
- package/components/theme-editor/sections/countdown-campaign-banner-section.tsx +433 -0
- package/components/theme-editor/sections/coupon-banner-section.tsx +710 -0
- package/components/theme-editor/sections/divider-section.tsx +62 -0
- package/components/theme-editor/sections/featured-product-spotlight-section.tsx +507 -0
- package/components/theme-editor/sections/find-in-store-section.tsx +1995 -0
- package/components/theme-editor/sections/hover-showcase-section.tsx +326 -0
- package/components/theme-editor/sections/image-hotspot-section.tsx +142 -0
- package/components/theme-editor/sections/installment-options-section.tsx +1065 -0
- package/components/theme-editor/sections/notification-banner-section.tsx +173 -0
- package/components/theme-editor/sections/order-tracking-lookup-section.tsx +1379 -0
- package/components/theme-editor/sections/posts-slider-section.tsx +472 -0
- package/components/theme-editor/sections/pre-order-launch-banner-section.tsx +663 -0
- package/components/theme-editor/sections/section-renderer-registry.tsx +89 -0
- package/components/theme-editor/sections/section-wrapper.tsx +135 -0
- package/components/theme-editor/sections/shipping-threshold-progress-section.tsx +586 -0
- package/components/theme-editor/sections/stats-counter-section.tsx +486 -0
- package/components/theme-editor/sections/tabs-section.tsx +578 -0
- package/components/theme-editor/theme-block.tsx +102 -0
- package/components/theme-editor/theme-placeholder-client.tsx +218 -0
- package/components/theme-editor/theme-placeholder-wrapper.tsx +732 -0
- package/components/theme-editor/theme-placeholder.tsx +288 -0
- package/components/theme-editor/theme-section.tsx +1224 -0
- package/components/theme-editor/theme-settings-context.tsx +13 -0
- package/components/theme-editor/utils/index.ts +792 -0
- package/components/theme-editor/utils/iterator-utils.ts +234 -0
- package/components/theme-editor/utils/publish-window.ts +86 -0
- package/components/theme-editor/utils/visibility-rules.ts +188 -0
- package/data/client/account.ts +17 -2
- package/data/client/api.ts +2 -0
- package/data/client/basket.ts +66 -5
- package/data/client/checkout.ts +391 -99
- package/data/client/misc.ts +38 -2
- package/data/client/product.ts +19 -2
- package/data/client/user.ts +16 -8
- package/data/server/category.ts +11 -9
- package/data/server/flatpage.ts +11 -4
- package/data/server/form.ts +15 -4
- package/data/server/landingpage.ts +11 -4
- package/data/server/list.ts +5 -4
- package/data/server/menu.ts +11 -3
- package/data/server/product.ts +111 -55
- package/data/server/seo.ts +14 -4
- package/data/server/special-page.ts +5 -4
- package/data/server/widget.ts +90 -5
- package/data/urls.ts +16 -5
- package/hocs/client/with-segment-defaults.tsx +2 -2
- package/hocs/server/with-segment-defaults.tsx +65 -20
- package/hooks/index.ts +4 -0
- package/hooks/use-localization.ts +24 -10
- package/hooks/use-logger-context.tsx +114 -0
- package/hooks/use-logger.ts +92 -0
- package/hooks/use-loyalty-availability.ts +21 -0
- package/hooks/use-payment-options.ts +2 -1
- package/hooks/use-pz-params.ts +37 -0
- package/hooks/use-router.ts +51 -14
- package/hooks/use-sentry-uncaught-errors.ts +24 -0
- package/instrumentation/index.ts +10 -1
- package/instrumentation/node.ts +2 -20
- package/jest.config.js +25 -0
- package/lib/cache-handler.mjs +534 -16
- package/lib/cache.ts +272 -37
- package/localization/index.ts +2 -1
- package/localization/provider.tsx +2 -5
- package/middlewares/bfcache-headers.ts +18 -0
- package/middlewares/checkout-provider.ts +1 -1
- package/middlewares/complete-gpay.ts +32 -26
- package/middlewares/complete-masterpass.ts +33 -26
- package/middlewares/complete-wallet.ts +182 -0
- package/middlewares/default.ts +360 -215
- package/middlewares/index.ts +10 -2
- package/middlewares/locale.ts +34 -11
- package/middlewares/masterpass-rest-callback.ts +230 -0
- package/middlewares/oauth-login.ts +200 -57
- package/middlewares/pretty-url.ts +21 -8
- package/middlewares/redirection-payment.ts +32 -26
- package/middlewares/saved-card-redirection.ts +33 -26
- package/middlewares/three-d-redirection.ts +32 -26
- package/middlewares/url-redirection.ts +11 -1
- package/middlewares/wallet-complete-redirection.ts +206 -0
- package/package.json +25 -10
- package/plugins.d.ts +19 -4
- package/plugins.js +10 -1
- package/redux/actions.ts +47 -0
- package/redux/middlewares/checkout.ts +63 -138
- package/redux/middlewares/index.ts +14 -10
- package/redux/middlewares/pre-order/address.ts +7 -2
- package/redux/middlewares/pre-order/attribute-based-shipping-option.ts +7 -1
- package/redux/middlewares/pre-order/data-source-shipping-option.ts +7 -1
- package/redux/middlewares/pre-order/delivery-option.ts +7 -1
- package/redux/middlewares/pre-order/index.ts +16 -10
- package/redux/middlewares/pre-order/installment-option.ts +8 -1
- package/redux/middlewares/pre-order/payment-option-reset.ts +37 -0
- package/redux/middlewares/pre-order/payment-option.ts +7 -1
- package/redux/middlewares/pre-order/pre-order-validation.ts +8 -3
- package/redux/middlewares/pre-order/redirection.ts +8 -2
- package/redux/middlewares/pre-order/set-pre-order.ts +6 -2
- package/redux/middlewares/pre-order/shipping-option.ts +7 -1
- package/redux/middlewares/pre-order/shipping-step.ts +5 -1
- package/redux/reducers/checkout.ts +23 -3
- package/redux/reducers/index.ts +11 -3
- package/redux/reducers/root.ts +7 -2
- package/redux/reducers/widget.ts +80 -0
- package/sentry/index.ts +69 -13
- package/tailwind/content.js +16 -0
- package/types/commerce/account.ts +5 -1
- package/types/commerce/checkout.ts +35 -1
- package/types/commerce/widget.ts +33 -0
- package/types/index.ts +101 -6
- package/types/next-auth.d.ts +2 -2
- package/types/widget.ts +80 -0
- package/utils/app-fetch.ts +7 -2
- package/utils/generate-commerce-search-params.ts +3 -2
- package/utils/get-checkout-path.ts +3 -0
- package/utils/get-root-hostname.ts +28 -0
- package/utils/index.ts +64 -10
- package/utils/localization.ts +4 -0
- package/utils/mobile-3d-iframe.ts +8 -2
- package/utils/override-middleware.ts +7 -12
- package/utils/pz-segments.ts +92 -0
- package/utils/redirect-ignore.ts +35 -0
- package/utils/redirect.ts +9 -3
- package/utils/redirection-iframe.ts +8 -2
- package/utils/widget-styles.ts +107 -0
- package/views/error-page.tsx +93 -0
- 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:
|
|
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:
|
|
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
|
};
|
package/data/server/widget.ts
CHANGED
|
@@ -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
|
-
(
|
|
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({
|
|
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 {
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
61
|
+
componentProps as ResolvedRootLayoutProps,
|
|
62
|
+
localeValue
|
|
30
63
|
)) as T;
|
|
31
64
|
|
|
32
65
|
checkRedisVariables();
|
|
33
66
|
}
|
|
34
67
|
|
|
35
|
-
ServerVariables.locale =
|
|
36
|
-
ServerVariables.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 (
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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(
|
|
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 ===
|
|
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
|
|
29
|
-
|
|
30
|
-
localeUrlStrategy !== LocaleUrlStrategy.ShowAllLocales
|
|
31
|
-
? ''
|
|
32
|
-
: `/${locale}`;
|
|
28
|
+
const { protocol, hostname, port, search, pathname } = location;
|
|
29
|
+
const pathnameWithoutLocale = pathname.replace(urlLocaleMatcherRegex, '');
|
|
33
30
|
|
|
34
|
-
|
|
35
|
-
|
|
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 =
|
|
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);
|