@akinon/next 1.45.0 → 1.47.0-rc.0
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/CHANGELOG.md +367 -0
- package/api/client.ts +39 -9
- package/assets/styles/index.css +49 -0
- package/assets/styles/index.css.map +1 -0
- package/assets/styles/index.scss +50 -26
- package/components/file-input.tsx +8 -0
- package/components/index.ts +1 -0
- package/components/input.tsx +21 -7
- package/components/link.tsx +17 -13
- package/components/pagination.tsx +1 -2
- package/components/price.tsx +11 -4
- package/components/selected-payment-option-view.tsx +26 -38
- package/data/client/account.ts +10 -9
- package/data/client/address.ts +32 -8
- package/data/client/api.ts +2 -2
- package/data/client/b2b.ts +35 -2
- package/data/client/basket.ts +6 -5
- package/data/client/checkout.ts +47 -4
- package/data/client/wishlist.ts +70 -2
- package/data/server/category.ts +2 -2
- package/data/server/list.ts +2 -2
- package/data/server/product.ts +15 -13
- package/data/server/special-page.ts +2 -2
- package/data/urls.ts +17 -3
- package/hooks/index.ts +2 -1
- package/hooks/use-message-listener.ts +24 -0
- package/hooks/use-payment-options.ts +2 -1
- package/lib/cache-handler.mjs +33 -0
- package/lib/cache.ts +18 -6
- package/middlewares/default.ts +50 -2
- package/middlewares/locale.ts +32 -30
- package/middlewares/pretty-url.ts +4 -0
- package/middlewares/url-redirection.ts +4 -0
- package/package.json +5 -4
- package/plugins.d.ts +1 -0
- package/redux/middlewares/checkout.ts +70 -11
- package/redux/reducers/checkout.ts +24 -5
- package/redux/reducers/config.ts +2 -0
- package/routes/pretty-url.tsx +194 -0
- package/types/commerce/account.ts +1 -0
- package/types/commerce/address.ts +1 -1
- package/types/commerce/b2b.ts +12 -2
- package/types/commerce/checkout.ts +30 -0
- package/types/commerce/misc.ts +2 -0
- package/types/commerce/order.ts +12 -0
- package/types/index.ts +30 -2
- package/utils/app-fetch.ts +1 -1
- package/utils/generate-commerce-search-params.ts +6 -2
- package/utils/index.ts +27 -6
- package/utils/menu-generator.ts +2 -2
- package/utils/redirection-iframe.ts +85 -0
- package/utils/server-translation.ts +5 -1
- package/with-pz-config.js +11 -1
package/data/server/product.ts
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import { Cache, CacheKey } from '../../lib/cache';
|
|
2
2
|
import { product } from '../urls';
|
|
3
|
-
import {
|
|
4
|
-
BreadcrumbResultType,
|
|
5
|
-
ProductCategoryResult,
|
|
6
|
-
ProductResult
|
|
7
|
-
} from '../../types';
|
|
3
|
+
import { ProductCategoryResult, ProductResult } from '../../types';
|
|
8
4
|
import appFetch from '../../utils/app-fetch';
|
|
5
|
+
import logger from '../../utils/log';
|
|
9
6
|
|
|
10
7
|
type GetProduct = {
|
|
11
8
|
pk: number;
|
|
12
|
-
searchParams?:
|
|
9
|
+
searchParams?: { [key: string]: string | string[] | undefined };
|
|
13
10
|
groupProduct?: boolean;
|
|
14
11
|
};
|
|
15
12
|
|
|
@@ -50,9 +47,17 @@ const getProductDataHandler = ({
|
|
|
50
47
|
}
|
|
51
48
|
);
|
|
52
49
|
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
)
|
|
50
|
+
const menuItemModel = productCategoryData?.results[0]?.menuitemmodel;
|
|
51
|
+
|
|
52
|
+
if (!menuItemModel) {
|
|
53
|
+
logger.warn('menuItemModel is undefined, skipping breadcrumbData fetch', {
|
|
54
|
+
handler: 'getProductDataHandler',
|
|
55
|
+
pk
|
|
56
|
+
});
|
|
57
|
+
return { data, breadcrumbData: undefined };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const breadcrumbUrl = product.breadcrumbUrl(menuItemModel);
|
|
56
61
|
|
|
57
62
|
const breadcrumbData = await appFetch<any>(breadcrumbUrl, {
|
|
58
63
|
headers: {
|
|
@@ -74,10 +79,7 @@ export const getProductData = async ({
|
|
|
74
79
|
groupProduct
|
|
75
80
|
}: GetProduct) => {
|
|
76
81
|
return Cache.wrap(
|
|
77
|
-
CacheKey[groupProduct ? 'GroupProduct' : 'Product'](
|
|
78
|
-
pk,
|
|
79
|
-
searchParams ?? new URLSearchParams()
|
|
80
|
-
),
|
|
82
|
+
CacheKey[groupProduct ? 'GroupProduct' : 'Product'](pk, searchParams ?? {}),
|
|
81
83
|
getProductDataHandler({ pk, searchParams, groupProduct }),
|
|
82
84
|
{
|
|
83
85
|
expire: 300
|
|
@@ -7,7 +7,7 @@ import header from '../../redux/reducers/header';
|
|
|
7
7
|
|
|
8
8
|
const getSpecialPageDataHandler = (
|
|
9
9
|
pk: number,
|
|
10
|
-
searchParams:
|
|
10
|
+
searchParams: { [key: string]: string | string[] | undefined },
|
|
11
11
|
headers?: Record<string, string>
|
|
12
12
|
) => {
|
|
13
13
|
return async function () {
|
|
@@ -34,7 +34,7 @@ export const getSpecialPageData = async ({
|
|
|
34
34
|
headers
|
|
35
35
|
}: {
|
|
36
36
|
pk: number;
|
|
37
|
-
searchParams:
|
|
37
|
+
searchParams: { [key: string]: string | string[] | undefined };
|
|
38
38
|
headers?: Record<string, string>;
|
|
39
39
|
}) => {
|
|
40
40
|
return Cache.wrap(
|
package/data/urls.ts
CHANGED
|
@@ -83,6 +83,8 @@ export const checkout = {
|
|
|
83
83
|
setDeliveryOption: '/orders/checkout/?page=DeliveryOptionSelectionPage',
|
|
84
84
|
setAddresses: '/orders/checkout/?page=AddressSelectionPage',
|
|
85
85
|
setShippingOption: '/orders/checkout/?page=ShippingOptionSelectionPage',
|
|
86
|
+
setDataSourceShippingOption:
|
|
87
|
+
'/orders/checkout/?page=DataSourceShippingOptionSelectionPage',
|
|
86
88
|
setPaymentOption: '/orders/checkout/?page=PaymentOptionSelectionPage',
|
|
87
89
|
setBinNumber: '/orders/checkout/?page=BinNumberPage',
|
|
88
90
|
setMasterpassBinNumber: '/orders/checkout/?page=MasterpassBinNumberPage',
|
|
@@ -108,7 +110,9 @@ export const checkout = {
|
|
|
108
110
|
completeLoyaltyPayment: '/orders/checkout/?page=LoyaltyPaymentSelectedPage',
|
|
109
111
|
loyaltyMoneyUsage: '/orders/checkout/?page=LoyaltyMoneyUsagePage',
|
|
110
112
|
setOrderNote: '/orders/checkout/?page=OrderNotePage',
|
|
111
|
-
couponSelectionPage: '/orders/checkout/?page=CouponSelectionPage'
|
|
113
|
+
couponSelectionPage: '/orders/checkout/?page=CouponSelectionPage',
|
|
114
|
+
setAttributeBasedShippingOption:
|
|
115
|
+
'/orders/checkout/?page=AttributeBasedShippingOptionSelectionPage'
|
|
112
116
|
};
|
|
113
117
|
|
|
114
118
|
export const flatpage = {
|
|
@@ -161,7 +165,14 @@ export const wishlist = {
|
|
|
161
165
|
page?: number;
|
|
162
166
|
limit?: number;
|
|
163
167
|
}) => `/wishlists/product-alerts/?limit=${limit}&page=${page}`,
|
|
164
|
-
deleteStockAlert: (pk: number) => `/wishlists/product-alerts/${pk}
|
|
168
|
+
deleteStockAlert: (pk: number) => `/wishlists/product-alerts/${pk}/`,
|
|
169
|
+
setCollection: () => '/wishlists/user-collections/',
|
|
170
|
+
setCollectionItems: '/wishlists/user-collection-items/',
|
|
171
|
+
getCollections: (search?: string, product_id?: number) =>
|
|
172
|
+
`/wishlists/user-collections/?search=${search ? search : ''}&product_id=${
|
|
173
|
+
product_id ? product_id : ''
|
|
174
|
+
}`,
|
|
175
|
+
deleteCollection: (pk: number) => `/wishlists/user-collections/${pk}/`
|
|
165
176
|
};
|
|
166
177
|
|
|
167
178
|
export const user = {
|
|
@@ -187,7 +198,10 @@ export const b2b = {
|
|
|
187
198
|
draftBaskets: '/b2b/basket/drafts/',
|
|
188
199
|
divisions: '/b2b/my-divisions/',
|
|
189
200
|
myQuotations: '/b2b/my-quotations/',
|
|
190
|
-
loadBasket: (id) => `/b2b/basket/${id}/load
|
|
201
|
+
loadBasket: (id) => `/b2b/basket/${id}/load/`,
|
|
202
|
+
statusBasket: (cacheKey) => `/b2b/basket/?status_cache_key=${cacheKey}`,
|
|
203
|
+
basketExport: (queryString) => `/b2b/basket/?upload=true&${queryString}`,
|
|
204
|
+
basketImport: '/b2b/basket/bulk-import/'
|
|
191
205
|
};
|
|
192
206
|
|
|
193
207
|
export const widgets = {
|
package/hooks/index.ts
CHANGED
|
@@ -8,4 +8,5 @@ export * from './use-media-query';
|
|
|
8
8
|
export * from './use-on-click-outside';
|
|
9
9
|
export * from './use-mobile-iframe-handler';
|
|
10
10
|
export * from './use-payment-options';
|
|
11
|
-
export * from './use-pagination';
|
|
11
|
+
export * from './use-pagination';
|
|
12
|
+
export * from './use-message-listener';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
export const useMessageListener = () => {
|
|
4
|
+
useEffect(() => {
|
|
5
|
+
const handleMessage = (event: MessageEvent) => {
|
|
6
|
+
if (event.origin !== window.location.origin) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (event.data && typeof event.data === 'string') {
|
|
11
|
+
const messageData = JSON.parse(event.data);
|
|
12
|
+
if (messageData?.url) {
|
|
13
|
+
window.location.href = messageData.url;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
window.addEventListener('message', handleMessage);
|
|
19
|
+
|
|
20
|
+
return () => {
|
|
21
|
+
window.removeEventListener('message', handleMessage);
|
|
22
|
+
};
|
|
23
|
+
}, []);
|
|
24
|
+
};
|
|
@@ -18,7 +18,8 @@ export const usePaymentOptions = () => {
|
|
|
18
18
|
pay_on_delivery: 'pz-pay-on-delivery',
|
|
19
19
|
bkm_express: 'pz-bkm',
|
|
20
20
|
credit_payment: 'pz-credit-payment',
|
|
21
|
-
masterpass: 'pz-masterpass'
|
|
21
|
+
masterpass: 'pz-masterpass',
|
|
22
|
+
gpay: 'pz-gpay'
|
|
22
23
|
};
|
|
23
24
|
|
|
24
25
|
const isInitialTypeIncluded = (type: string) => initialTypes.has(type);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { CacheHandler } from '@neshca/cache-handler';
|
|
2
|
+
import createLruHandler from '@neshca/cache-handler/local-lru';
|
|
3
|
+
import createRedisHandler from '@neshca/cache-handler/redis-stack';
|
|
4
|
+
import { createClient } from 'redis';
|
|
5
|
+
|
|
6
|
+
CacheHandler.onCreation(async () => {
|
|
7
|
+
const redisUrl = `redis://${process.env.CACHE_HOST}:${
|
|
8
|
+
process.env.CACHE_PORT
|
|
9
|
+
}/${process.env.CACHE_BUCKET ?? '0'}`;
|
|
10
|
+
|
|
11
|
+
const client = createClient({
|
|
12
|
+
url: redisUrl
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
client.on('error', (error) => {
|
|
16
|
+
console.error('Redis client error', { redisUrl, error });
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
await client.connect();
|
|
20
|
+
|
|
21
|
+
const redisHandler = await createRedisHandler({
|
|
22
|
+
client,
|
|
23
|
+
timeoutMs: 5000
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const localHandler = createLruHandler();
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
handlers: [redisHandler, localHandler]
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export default CacheHandler;
|
package/lib/cache.ts
CHANGED
|
@@ -20,13 +20,16 @@ const hashCacheKey = (object?: Record<string, string>) => {
|
|
|
20
20
|
return `_${encodeURIComponent(cacheKey)}`;
|
|
21
21
|
};
|
|
22
22
|
export const CacheKey = {
|
|
23
|
-
List: (
|
|
23
|
+
List: (
|
|
24
|
+
searchParams: { [key: string]: string | string[] | undefined },
|
|
25
|
+
headers?: Record<string, string>
|
|
26
|
+
) =>
|
|
24
27
|
`list_${encodeURIComponent(JSON.stringify(searchParams))}${hashCacheKey(
|
|
25
28
|
headers
|
|
26
29
|
)}`,
|
|
27
30
|
Category: (
|
|
28
31
|
pk: number,
|
|
29
|
-
searchParams?:
|
|
32
|
+
searchParams?: { [key: string]: string | string[] | undefined },
|
|
30
33
|
headers?: Record<string, string>
|
|
31
34
|
) =>
|
|
32
35
|
`category_${pk}_${encodeURIComponent(
|
|
@@ -35,15 +38,20 @@ export const CacheKey = {
|
|
|
35
38
|
CategorySlug: (slug: string) => `category_${slug}`,
|
|
36
39
|
SpecialPage: (
|
|
37
40
|
pk: number,
|
|
38
|
-
searchParams:
|
|
41
|
+
searchParams: { [key: string]: string | string[] | undefined },
|
|
39
42
|
headers?: Record<string, string>
|
|
40
43
|
) =>
|
|
41
44
|
`special_page_${pk}_${encodeURIComponent(
|
|
42
45
|
JSON.stringify(searchParams)
|
|
43
46
|
)}${hashCacheKey(headers)}`,
|
|
44
|
-
Product: (
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
Product: (
|
|
48
|
+
pk: number,
|
|
49
|
+
searchParams: { [key: string]: string | string[] | undefined }
|
|
50
|
+
) => `product_${pk}_${encodeURIComponent(JSON.stringify(searchParams))}`,
|
|
51
|
+
GroupProduct: (
|
|
52
|
+
pk: number,
|
|
53
|
+
searchParams: { [key: string]: string | string[] | undefined }
|
|
54
|
+
) =>
|
|
47
55
|
`group_product_${pk}_${encodeURIComponent(JSON.stringify(searchParams))}`,
|
|
48
56
|
FlatPage: (pk: number) => `flat_page_${pk}`,
|
|
49
57
|
LandingPage: (pk: number) => `landing_page_${pk}`,
|
|
@@ -158,6 +166,10 @@ export class Cache {
|
|
|
158
166
|
handler: () => Promise<T>,
|
|
159
167
|
options?: CacheOptions
|
|
160
168
|
): Promise<T> {
|
|
169
|
+
if (Settings.usePrettyUrlRoute) {
|
|
170
|
+
return await handler();
|
|
171
|
+
}
|
|
172
|
+
|
|
161
173
|
const requiredVariables = [
|
|
162
174
|
process.env.CACHE_HOST,
|
|
163
175
|
process.env.CACHE_PORT,
|
package/middlewares/default.ts
CHANGED
|
@@ -69,6 +69,10 @@ const withPzDefault =
|
|
|
69
69
|
return NextResponse.json({ status: 'ok' });
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
if (url.pathname.startsWith('/metrics')) {
|
|
73
|
+
return new NextResponse(null, { status: 200 });
|
|
74
|
+
}
|
|
75
|
+
|
|
72
76
|
if (req.nextUrl.pathname.startsWith('/.well-known')) {
|
|
73
77
|
const url = new URL(`${Settings.commerceUrl}${req.nextUrl.pathname}`);
|
|
74
78
|
const req_ = await fetch(url.toString());
|
|
@@ -84,14 +88,44 @@ const withPzDefault =
|
|
|
84
88
|
req.nextUrl.pathname.includes('/orders/hooks/') ||
|
|
85
89
|
req.nextUrl.pathname.includes('/orders/checkout-with-token/')
|
|
86
90
|
) {
|
|
87
|
-
|
|
91
|
+
const segment = url.searchParams.get('segment');
|
|
92
|
+
const currency = url.searchParams.get('currency')?.toLowerCase();
|
|
93
|
+
|
|
94
|
+
const headers = {};
|
|
95
|
+
|
|
96
|
+
if (segment) {
|
|
97
|
+
headers['X-Segment-Id'] = segment;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (currency) {
|
|
101
|
+
headers['x-currency'] = currency;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const response = NextResponse.rewrite(
|
|
88
105
|
new URL(
|
|
89
106
|
`${Settings.commerceUrl}${req.nextUrl.pathname.replace(
|
|
90
107
|
urlLocaleMatcherRegex,
|
|
91
108
|
''
|
|
92
109
|
)}`
|
|
93
|
-
)
|
|
110
|
+
),
|
|
111
|
+
{
|
|
112
|
+
headers
|
|
113
|
+
}
|
|
94
114
|
);
|
|
115
|
+
|
|
116
|
+
if (segment) {
|
|
117
|
+
response.cookies.set('pz-segment', segment);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (currency) {
|
|
121
|
+
response.cookies.set('pz-currency', currency, {
|
|
122
|
+
sameSite: 'none',
|
|
123
|
+
secure: true,
|
|
124
|
+
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 7) // 7 days
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return response;
|
|
95
129
|
}
|
|
96
130
|
|
|
97
131
|
if (req.nextUrl.pathname.startsWith('/orders/redirection/')) {
|
|
@@ -203,6 +237,20 @@ const withPzDefault =
|
|
|
203
237
|
);
|
|
204
238
|
}
|
|
205
239
|
|
|
240
|
+
if (
|
|
241
|
+
Settings.usePrettyUrlRoute &&
|
|
242
|
+
url.searchParams.toString().length > 0 &&
|
|
243
|
+
!Object.entries(ROUTES).find(([, value]) =>
|
|
244
|
+
new RegExp(`^${value}/?$`).test(
|
|
245
|
+
pathnameWithoutLocale
|
|
246
|
+
)
|
|
247
|
+
)
|
|
248
|
+
) {
|
|
249
|
+
url.pathname =
|
|
250
|
+
url.pathname +
|
|
251
|
+
`searchparams|${url.searchParams.toString()}`;
|
|
252
|
+
}
|
|
253
|
+
|
|
206
254
|
Settings.rewrites.forEach((rewrite) => {
|
|
207
255
|
url.pathname = url.pathname.replace(
|
|
208
256
|
rewrite.source,
|
package/middlewares/locale.ts
CHANGED
|
@@ -5,22 +5,6 @@ import { LocaleUrlStrategy } from '../localization';
|
|
|
5
5
|
import { urlLocaleMatcherRegex } from '../utils';
|
|
6
6
|
import logger from '../utils/log';
|
|
7
7
|
|
|
8
|
-
const getMatchedLocale = (pathname: string) => {
|
|
9
|
-
let matchedLocale = pathname.match(urlLocaleMatcherRegex)?.[0] ?? '';
|
|
10
|
-
matchedLocale = matchedLocale.replace('/', '');
|
|
11
|
-
|
|
12
|
-
if (!matchedLocale.length) {
|
|
13
|
-
if (
|
|
14
|
-
settings.localization.localeUrlStrategy !==
|
|
15
|
-
LocaleUrlStrategy.ShowAllLocales
|
|
16
|
-
) {
|
|
17
|
-
matchedLocale = settings.localization.defaultLocaleValue;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return matchedLocale;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
8
|
const withLocale =
|
|
25
9
|
(middleware: NextMiddleware) =>
|
|
26
10
|
async (req: PzNextRequest, event: NextFetchEvent) => {
|
|
@@ -28,12 +12,12 @@ const withLocale =
|
|
|
28
12
|
|
|
29
13
|
try {
|
|
30
14
|
const url = req.nextUrl.clone();
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
15
|
+
const {
|
|
16
|
+
locales,
|
|
17
|
+
localeUrlStrategy,
|
|
18
|
+
defaultLocaleValue,
|
|
19
|
+
redirectToDefaultLocale
|
|
20
|
+
} = settings.localization;
|
|
37
21
|
|
|
38
22
|
if (!defaultLocaleValue) {
|
|
39
23
|
logger.error('Default locale value is not defined in settings.', {
|
|
@@ -45,16 +29,34 @@ const withLocale =
|
|
|
45
29
|
}
|
|
46
30
|
|
|
47
31
|
if (
|
|
48
|
-
|
|
49
|
-
localeUrlStrategy === LocaleUrlStrategy.ShowAllLocales
|
|
50
|
-
redirectToDefaultLocale &&
|
|
51
|
-
req.method === 'GET'
|
|
32
|
+
locales.length === 1 &&
|
|
33
|
+
localeUrlStrategy === LocaleUrlStrategy.ShowAllLocales
|
|
52
34
|
) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
35
|
+
const singleLocale = locales[0].value;
|
|
36
|
+
const pathParts = url.pathname.split('/').filter(Boolean);
|
|
56
37
|
|
|
57
|
-
|
|
38
|
+
if (pathParts[0] === singleLocale) {
|
|
39
|
+
url.pathname = '/' + pathParts.slice(1).join('/');
|
|
40
|
+
return NextResponse.redirect(url);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
req.middlewareParams.rewrites.locale = singleLocale;
|
|
44
|
+
} else {
|
|
45
|
+
const matchedLocale =
|
|
46
|
+
url.pathname.match(urlLocaleMatcherRegex)?.[0]?.replace('/', '') ||
|
|
47
|
+
'';
|
|
48
|
+
if (
|
|
49
|
+
!matchedLocale &&
|
|
50
|
+
localeUrlStrategy === LocaleUrlStrategy.ShowAllLocales &&
|
|
51
|
+
redirectToDefaultLocale &&
|
|
52
|
+
req.method === 'GET'
|
|
53
|
+
) {
|
|
54
|
+
url.pathname = `/${defaultLocaleValue}${url.pathname}`;
|
|
55
|
+
return NextResponse.redirect(url);
|
|
56
|
+
}
|
|
57
|
+
req.middlewareParams.rewrites.locale =
|
|
58
|
+
matchedLocale || defaultLocaleValue;
|
|
59
|
+
}
|
|
58
60
|
} catch (error) {
|
|
59
61
|
logger.error('withLocale error', {
|
|
60
62
|
error,
|
|
@@ -56,6 +56,10 @@ const resolvePrettyUrl = async (pathname: string, ip: string | null) => {
|
|
|
56
56
|
const withPrettyUrl =
|
|
57
57
|
(middleware: NextMiddleware) =>
|
|
58
58
|
async (req: PzNextRequest, event: NextFetchEvent) => {
|
|
59
|
+
if (Settings.usePrettyUrlRoute) {
|
|
60
|
+
return middleware(req, event);
|
|
61
|
+
}
|
|
62
|
+
|
|
59
63
|
const url = req.nextUrl.clone();
|
|
60
64
|
const matchedLanguagePrefix = url.pathname.match(
|
|
61
65
|
urlLocaleMatcherRegex
|
|
@@ -11,6 +11,10 @@ import { ROUTES } from 'routes';
|
|
|
11
11
|
const withUrlRedirection =
|
|
12
12
|
(middleware: NextMiddleware) =>
|
|
13
13
|
async (req: PzNextRequest, event: NextFetchEvent) => {
|
|
14
|
+
if (settings.usePrettyUrlRoute) {
|
|
15
|
+
return middleware(req, event);
|
|
16
|
+
}
|
|
17
|
+
|
|
14
18
|
const url = req.nextUrl.clone();
|
|
15
19
|
const ip = req.headers.get('x-forwarded-for') ?? '';
|
|
16
20
|
const pathnameWithoutLocale = url.pathname.replace(
|
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": "1.
|
|
4
|
+
"version": "1.47.0-rc.0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
@@ -20,21 +20,22 @@
|
|
|
20
20
|
"@opentelemetry/sdk-trace-node": "1.19.0",
|
|
21
21
|
"@opentelemetry/semantic-conventions": "1.19.0",
|
|
22
22
|
"@reduxjs/toolkit": "1.9.7",
|
|
23
|
+
"@neshca/cache-handler": "1.5.1",
|
|
23
24
|
"cross-spawn": "7.0.3",
|
|
24
25
|
"generic-pool": "3.9.0",
|
|
25
26
|
"react-redux": "8.1.3",
|
|
26
27
|
"react-string-replace": "1.1.1",
|
|
27
|
-
"redis": "4.
|
|
28
|
-
"semver": "7.
|
|
28
|
+
"redis": "4.6.13",
|
|
29
|
+
"semver": "7.6.2",
|
|
29
30
|
"set-cookie-parser": "2.6.0"
|
|
30
31
|
},
|
|
31
32
|
"devDependencies": {
|
|
33
|
+
"@akinon/eslint-plugin-projectzero": "1.47.0-rc.0",
|
|
32
34
|
"@types/react-redux": "7.1.30",
|
|
33
35
|
"@types/set-cookie-parser": "2.4.7",
|
|
34
36
|
"@typescript-eslint/eslint-plugin": "6.7.4",
|
|
35
37
|
"@typescript-eslint/parser": "6.7.4",
|
|
36
38
|
"eslint": "^8.14.0",
|
|
37
|
-
"@akinon/eslint-plugin-projectzero": "1.45.0",
|
|
38
39
|
"eslint-config-prettier": "8.5.0"
|
|
39
40
|
}
|
|
40
41
|
}
|
package/plugins.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ declare module '@akinon/pz-masterpass/src/utils' {
|
|
|
12
12
|
declare module '@akinon/pz-masterpass/src/redux/reducer' {
|
|
13
13
|
export const setError: any;
|
|
14
14
|
export const setOtpModalVisible: any;
|
|
15
|
+
export const setCvcRequired: any;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
declare module '@akinon/pz-otp' {
|
|
@@ -16,8 +16,10 @@ import {
|
|
|
16
16
|
setPreOrder,
|
|
17
17
|
setRetailStores,
|
|
18
18
|
setShippingOptions,
|
|
19
|
+
setDataSourceShippingOptions,
|
|
19
20
|
setShippingStepCompleted,
|
|
20
|
-
setCreditPaymentOptions
|
|
21
|
+
setCreditPaymentOptions,
|
|
22
|
+
setAttributeBasedShippingOptions
|
|
21
23
|
} from '../../redux/reducers/checkout';
|
|
22
24
|
import { RootState, TypedDispatch } from 'redux/store';
|
|
23
25
|
import { checkoutApi } from '../../data/client/checkout';
|
|
@@ -26,10 +28,11 @@ import { getCookie, setCookie } from '../../utils';
|
|
|
26
28
|
import settings from 'settings';
|
|
27
29
|
import { LocaleUrlStrategy } from '../../localization';
|
|
28
30
|
import { showMobile3dIframe } from '../../utils/mobile-3d-iframe';
|
|
31
|
+
import { showRedirectionIframe } from '../../utils/redirection-iframe';
|
|
29
32
|
|
|
30
33
|
interface CheckoutResult {
|
|
31
34
|
payload: {
|
|
32
|
-
errors?:
|
|
35
|
+
errors?: Record<string, string[]>;
|
|
33
36
|
pre_order?: PreOrder;
|
|
34
37
|
context_list?: CheckoutContext[];
|
|
35
38
|
};
|
|
@@ -73,8 +76,10 @@ export const preOrderMiddleware: Middleware = ({
|
|
|
73
76
|
deliveryOptions,
|
|
74
77
|
addressList: addresses,
|
|
75
78
|
shippingOptions,
|
|
79
|
+
dataSourceShippingOptions,
|
|
76
80
|
paymentOptions,
|
|
77
|
-
installmentOptions
|
|
81
|
+
installmentOptions,
|
|
82
|
+
attributeBasedShippingOptions
|
|
78
83
|
} = getState().checkout;
|
|
79
84
|
const { endpoints: apiEndpoints } = checkoutApi;
|
|
80
85
|
|
|
@@ -125,6 +130,40 @@ export const preOrderMiddleware: Middleware = ({
|
|
|
125
130
|
dispatch(apiEndpoints.setShippingOption.initiate(shippingOptions[0].pk));
|
|
126
131
|
}
|
|
127
132
|
|
|
133
|
+
if (
|
|
134
|
+
dataSourceShippingOptions.length > 0 &&
|
|
135
|
+
!preOrder.data_source_shipping_options
|
|
136
|
+
) {
|
|
137
|
+
const selectedDataSourceShippingOptionsPks =
|
|
138
|
+
dataSourceShippingOptions.map(
|
|
139
|
+
(opt) => opt.data_source_shipping_options[0].pk
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
dispatch(
|
|
143
|
+
apiEndpoints.setDataSourceShippingOptions.initiate(
|
|
144
|
+
selectedDataSourceShippingOptionsPks
|
|
145
|
+
)
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (
|
|
150
|
+
Object.keys(attributeBasedShippingOptions).length > 0 &&
|
|
151
|
+
!preOrder.attribute_based_shipping_options
|
|
152
|
+
) {
|
|
153
|
+
const initialSelectedOptions: Record<string, number> = Object.fromEntries(
|
|
154
|
+
Object.entries(attributeBasedShippingOptions).map(([key, options]) => [
|
|
155
|
+
key,
|
|
156
|
+
options[0].pk
|
|
157
|
+
])
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
dispatch(
|
|
161
|
+
apiEndpoints.setAttributeBasedShippingOptions.initiate(
|
|
162
|
+
initialSelectedOptions
|
|
163
|
+
)
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
128
167
|
if (!preOrder.payment_option && paymentOptions.length > 0) {
|
|
129
168
|
dispatch(apiEndpoints.setPaymentOption.initiate(paymentOptions[0].pk));
|
|
130
169
|
}
|
|
@@ -163,6 +202,7 @@ export const contextListMiddleware: Middleware = ({
|
|
|
163
202
|
if (result?.payload?.context_list) {
|
|
164
203
|
result.payload.context_list.forEach((context) => {
|
|
165
204
|
const redirectUrl = context.page_context.redirect_url;
|
|
205
|
+
const isIframe = context.page_context.is_frame ?? false;
|
|
166
206
|
|
|
167
207
|
if (redirectUrl) {
|
|
168
208
|
const currentLocale = getCookie('pz-locale');
|
|
@@ -181,16 +221,19 @@ export const contextListMiddleware: Middleware = ({
|
|
|
181
221
|
}
|
|
182
222
|
|
|
183
223
|
const urlObj = new URL(url, window.location.origin);
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
/iPad|iPhone|iPod|Android/i.test(navigator.userAgent)) &&
|
|
224
|
+
const isMobileDevice =
|
|
225
|
+
isMobileApp ||
|
|
226
|
+
/iPad|iPhone|iPod|Android/i.test(navigator.userAgent);
|
|
227
|
+
const isIframePaymentOptionExcluded =
|
|
189
228
|
!settings.checkout?.iframeExcludedPaymentOptions?.includes(
|
|
190
229
|
result.payload?.pre_order?.payment_option?.slug
|
|
191
|
-
)
|
|
192
|
-
)
|
|
230
|
+
);
|
|
231
|
+
urlObj.searchParams.set('t', new Date().getTime().toString());
|
|
232
|
+
|
|
233
|
+
if (isMobileDevice && !isIframePaymentOptionExcluded) {
|
|
193
234
|
showMobile3dIframe(urlObj.toString());
|
|
235
|
+
} else if (isIframe && !isIframePaymentOptionExcluded) {
|
|
236
|
+
showRedirectionIframe(urlObj.toString());
|
|
194
237
|
} else {
|
|
195
238
|
window.location.href = urlObj.toString();
|
|
196
239
|
}
|
|
@@ -220,12 +263,28 @@ export const contextListMiddleware: Middleware = ({
|
|
|
220
263
|
dispatch(setShippingOptions(context.page_context.shipping_options));
|
|
221
264
|
}
|
|
222
265
|
|
|
266
|
+
if (context.page_context.data_sources) {
|
|
267
|
+
dispatch(
|
|
268
|
+
setDataSourceShippingOptions(context.page_context.data_sources)
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (context.page_context.attribute_based_shipping_options) {
|
|
273
|
+
dispatch(
|
|
274
|
+
setAttributeBasedShippingOptions(
|
|
275
|
+
context.page_context.attribute_based_shipping_options
|
|
276
|
+
)
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
223
280
|
if (context.page_context.payment_options) {
|
|
224
281
|
dispatch(setPaymentOptions(context.page_context.payment_options));
|
|
225
282
|
}
|
|
226
283
|
|
|
227
284
|
if (context.page_context.credit_payment_options) {
|
|
228
|
-
dispatch(
|
|
285
|
+
dispatch(
|
|
286
|
+
setCreditPaymentOptions(context.page_context.credit_payment_options)
|
|
287
|
+
);
|
|
229
288
|
}
|
|
230
289
|
|
|
231
290
|
if (context.page_context.payment_choices) {
|