@akinon/next 1.43.0 → 1.45.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.
Files changed (48) hide show
  1. package/CHANGELOG.md +244 -0
  2. package/api/client.ts +33 -9
  3. package/assets/styles/index.css +49 -0
  4. package/assets/styles/index.css.map +1 -0
  5. package/assets/styles/index.scss +50 -26
  6. package/components/input.tsx +21 -7
  7. package/components/link.tsx +17 -13
  8. package/components/pagination.tsx +1 -2
  9. package/components/price.tsx +11 -4
  10. package/components/selected-payment-option-view.tsx +26 -38
  11. package/data/client/account.ts +10 -9
  12. package/data/client/address.ts +32 -8
  13. package/data/client/api.ts +1 -1
  14. package/data/client/basket.ts +6 -9
  15. package/data/client/checkout.ts +47 -4
  16. package/data/server/category.ts +2 -2
  17. package/data/server/list.ts +2 -2
  18. package/data/server/product.ts +15 -13
  19. package/data/server/special-page.ts +2 -2
  20. package/data/urls.ts +5 -1
  21. package/hooks/index.ts +2 -1
  22. package/hooks/use-message-listener.ts +24 -0
  23. package/hooks/use-payment-options.ts +2 -1
  24. package/lib/cache-handler.mjs +33 -0
  25. package/lib/cache.ts +18 -6
  26. package/middlewares/default.ts +50 -2
  27. package/middlewares/locale.ts +32 -30
  28. package/middlewares/pretty-url.ts +4 -0
  29. package/middlewares/url-redirection.ts +4 -0
  30. package/package.json +5 -4
  31. package/plugins.d.ts +1 -0
  32. package/redux/middlewares/checkout.ts +70 -11
  33. package/redux/reducers/checkout.ts +24 -5
  34. package/redux/reducers/config.ts +2 -0
  35. package/routes/pretty-url.tsx +194 -0
  36. package/types/commerce/account.ts +1 -0
  37. package/types/commerce/address.ts +1 -1
  38. package/types/commerce/checkout.ts +30 -0
  39. package/types/commerce/misc.ts +2 -0
  40. package/types/commerce/order.ts +12 -0
  41. package/types/index.ts +28 -2
  42. package/utils/app-fetch.ts +1 -1
  43. package/utils/generate-commerce-search-params.ts +6 -2
  44. package/utils/index.ts +27 -6
  45. package/utils/menu-generator.ts +2 -2
  46. package/utils/redirection-iframe.ts +85 -0
  47. package/utils/server-translation.ts +5 -1
  48. package/with-pz-config.js +11 -1
@@ -6,6 +6,21 @@ import dynamic from 'next/dynamic';
6
6
  import { PaymentOptionViews } from 'views/checkout/steps/payment';
7
7
  import { useMemo } from 'react';
8
8
 
9
+ const fallbackView = () => <div />;
10
+
11
+ const paymentTypeToView = {
12
+ bkm_express: 'bkm',
13
+ credit_card: 'credit-card',
14
+ credit_payment: 'credit-payment',
15
+ funds_transfer: 'funds-transfer',
16
+ gpay: 'gpay',
17
+ loyalty_money: 'loyalty',
18
+ masterpass: 'credit-card',
19
+ pay_on_delivery: 'pay-on-delivery',
20
+ redirection: 'redirection'
21
+ // Add other mappings as needed
22
+ };
23
+
9
24
  export default function SelectedPaymentOptionView() {
10
25
  const { payment_option } = useAppSelector(
11
26
  (state: RootState) => state.checkout.preOrder
@@ -14,7 +29,7 @@ export default function SelectedPaymentOptionView() {
14
29
  const Component = useMemo(
15
30
  () =>
16
31
  dynamic(
17
- () => {
32
+ async () => {
18
33
  const customOption = PaymentOptionViews.find(
19
34
  (opt) => opt.slug === payment_option.slug
20
35
  );
@@ -29,46 +44,19 @@ export default function SelectedPaymentOptionView() {
29
44
  });
30
45
  }
31
46
 
32
- let promise: any;
33
-
34
- try {
35
- if (payment_option.payment_type === 'credit_card') {
36
- promise = import(
37
- `views/checkout/steps/payment/options/credit-card`
38
- );
39
- } else if (payment_option.payment_type === 'funds_transfer') {
40
- promise = import(
41
- `views/checkout/steps/payment/options/funds-transfer`
42
- );
43
- } else if (payment_option.payment_type === 'redirection') {
44
- promise = import(
45
- `views/checkout/steps/payment/options/redirection`
46
- );
47
- } else if (payment_option.payment_type === 'pay_on_delivery') {
48
- promise = import(
49
- `views/checkout/steps/payment/options/pay-on-delivery`
50
- );
51
- } else if (payment_option.payment_type === 'loyalty_money') {
52
- promise = import(`views/checkout/steps/payment/options/loyalty`);
53
- } else if (payment_option.payment_type === 'masterpass') {
54
- promise = import(
55
- `views/checkout/steps/payment/options/credit-card`
47
+ const view = paymentTypeToView[payment_option.payment_type];
48
+ if (view) {
49
+ try {
50
+ const mod = await import(
51
+ `views/checkout/steps/payment/options/${view}`
56
52
  );
53
+ return mod.default || fallbackView;
54
+ } catch (error) {
55
+ return fallbackView;
57
56
  }
58
- // else if (payment_option.payment_type === 'credit_payment') {
59
- // promise = import(`views/checkout/steps/payment/options/credit-payment`);
60
- // }
61
- // else if (payment_option.payment_type === 'gpay') {
62
- // promise = import(`views/checkout/steps/payment/options/gpay`);
63
- // }
64
- // else if (payment_option.payment_type === 'bkm_express') {
65
- // promise = import(`views/checkout/steps/payment/options/bkm`);
66
- // }
67
- } catch (error) {}
57
+ }
68
58
 
69
- return promise
70
- ? promise.then((mod) => mod.default).catch(() => () => null)
71
- : new Promise<any>((resolve) => resolve(() => null));
59
+ return fallbackView;
72
60
  },
73
61
  { ssr: false }
74
62
  ),
@@ -123,15 +123,16 @@ const accountApi = api.injectEndpoints({
123
123
  query: ({ page, status, limit }) =>
124
124
  buildClientRequestUrl(account.getQuotations(page, status, limit))
125
125
  }),
126
- sendContact: builder.mutation<void, ContactFormType>({
127
- query: (body) => ({
128
- url: buildClientRequestUrl(account.sendContact, {
129
- contentType: 'application/json',
130
- responseType: 'text'
131
- }),
132
- method: 'POST',
133
- body
134
- })
126
+ sendContact: builder.mutation<void, FormData>({
127
+ query: (body) => {
128
+ return {
129
+ url: buildClientRequestUrl(account.sendContact, {
130
+ useFormData: true
131
+ }),
132
+ method: 'POST',
133
+ body
134
+ };
135
+ }
135
136
  }),
136
137
  cancelOrder: builder.mutation<void, AccountOrderCancellation>({
137
138
  query: ({ id, ...body }) => ({
@@ -39,7 +39,10 @@ const addressApi = api.injectEndpoints({
39
39
  query: (city) =>
40
40
  buildClientRequestUrl(address.getRetailStoreTownships(city))
41
41
  }),
42
- addAddress: builder.mutation<Address, Partial<Address>>({
42
+ addAddress: builder.mutation<
43
+ Address,
44
+ Partial<Address> & { invalidateTag?: 'Addresses' | 'Checkout' }
45
+ >({
43
46
  query: (body) => ({
44
47
  url: buildClientRequestUrl(address.base, {
45
48
  contentType: 'application/json'
@@ -50,9 +53,16 @@ const addressApi = api.injectEndpoints({
50
53
  type: body.is_corporate === 'true' ? 'corporate' : 'personal'
51
54
  }
52
55
  }),
53
- invalidatesTags: (_, error) => (error ? [] : ['Addresses', 'Checkout'])
56
+ invalidatesTags: (_, error, arg) => {
57
+ if (error) return [];
58
+ if (arg.invalidateTag) return [arg.invalidateTag];
59
+ return ['Addresses', 'Checkout'];
60
+ }
54
61
  }),
55
- editAddress: builder.mutation<Address, Partial<Address>>({
62
+ editAddress: builder.mutation<
63
+ Address,
64
+ Partial<Address> & { invalidateTag?: 'Addresses' | 'Checkout' }
65
+ >({
56
66
  query: ({ pk, ...body }) => ({
57
67
  url: buildClientRequestUrl(address.editAddress(pk), {
58
68
  contentType: 'application/json'
@@ -64,14 +74,28 @@ const addressApi = api.injectEndpoints({
64
74
  type: body.is_corporate === 'true' ? 'corporate' : 'personal'
65
75
  }
66
76
  }),
67
- invalidatesTags: (_, error) => (error ? [] : ['Addresses', 'Checkout']) // TODO: Invalidate one of these tags when necessary (e.g. Address page invalidates Addresses tag, Checkout page invalidates Checkout tag)
77
+ invalidatesTags: (_, error, arg) => {
78
+ if (error) return [];
79
+ if (arg.invalidateTag) return [arg.invalidateTag];
80
+ return ['Addresses', 'Checkout'];
81
+ }
68
82
  }),
69
- removeAddress: builder.mutation<void, number>({
70
- query: (id) => ({
71
- url: buildClientRequestUrl(address.removeAddress(id)),
83
+ removeAddress: builder.mutation<
84
+ void,
85
+ number | { id: number; invalidateTag: 'Addresses' | 'Checkout' }
86
+ >({
87
+ query: (arg) => ({
88
+ url: buildClientRequestUrl(
89
+ address.removeAddress(typeof arg === 'number' ? arg : arg.id)
90
+ ),
72
91
  method: 'DELETE'
73
92
  }),
74
- invalidatesTags: (_, error) => (error ? [] : ['Addresses', 'Checkout']) // TODO: Invalidate one of these tags when necessary (e.g. Address page invalidates Addresses tag, Checkout page invalidates Checkout tag)
93
+ invalidatesTags: (_, error, arg) => {
94
+ if (error) return [];
95
+ if (typeof arg === 'object' && arg.invalidateTag)
96
+ return [arg.invalidateTag];
97
+ return ['Addresses', 'Checkout'];
98
+ }
75
99
  }),
76
100
  setDefaultAddress: builder.mutation<Address, Partial<Address>>({
77
101
  query: ({ pk, primary }) => ({
@@ -9,7 +9,7 @@ import {
9
9
  } from '@reduxjs/toolkit/query/react';
10
10
  import settings from 'settings';
11
11
  import { getCookie } from '../../utils';
12
- import { RootState } from '@theme/redux/store';
12
+ import { RootState } from 'redux/store';
13
13
 
14
14
  interface CustomBaseQueryApi extends BaseQueryApi {
15
15
  getState: () => RootState;
@@ -24,14 +24,11 @@ export const basketApi = api.injectEndpoints({
24
24
  transformResponse: (response: { basket: Basket }) => response.basket,
25
25
  providesTags: ['Basket']
26
26
  }),
27
- getBasketDetail: build.mutation<Basket, { namespace: string }>({
28
- query: ({ namespace }) => ({
29
- url: buildClientRequestUrl(basket.getBasketDetail(namespace), {
30
- contentType: 'application/json'
31
- }),
32
- method: 'GET'
33
- }),
34
- invalidatesTags: ['AllBaskets']
27
+ getBasketDetail: build.query<Basket, { namespace: string }>({
28
+ query: ({ namespace }) =>
29
+ buildClientRequestUrl(basket.getBasketDetail(namespace)),
30
+ transformResponse: (response: { basket: Basket }) => response.basket,
31
+ providesTags: ['AllBaskets']
35
32
  }),
36
33
  getAllBaskets: build.query<Basket[], void>({
37
34
  query: () =>
@@ -113,7 +110,7 @@ export const basketApi = api.injectEndpoints({
113
110
 
114
111
  export const {
115
112
  useGetBasketQuery,
116
- useGetBasketDetailMutation,
113
+ useLazyGetBasketDetailQuery,
117
114
  useGetAllBasketsQuery,
118
115
  useRemoveBasketMutation,
119
116
  useSelectMainBasketMutation,
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  setBankAccounts,
3
+ setCardType,
3
4
  setInstallmentOptions,
4
5
  setPaymentStepBusy,
5
6
  setSelectedBankAccountPk,
6
7
  setSelectedCreditPaymentPk,
7
- setShippingStepBusy,
8
- setCardType
8
+ setShippingStepBusy
9
9
  } from '../../redux/reducers/checkout';
10
10
  import {
11
11
  CheckoutContext,
@@ -20,6 +20,7 @@ import { AppDispatch, AppStore, store } from 'redux/store';
20
20
  import settings from 'settings';
21
21
  import { showMobile3dIframe } from '../../utils/mobile-3d-iframe';
22
22
  import {
23
+ setCvcRequired,
23
24
  setError,
24
25
  setOtpModalVisible
25
26
  } from '@akinon/pz-masterpass/src/redux/reducer';
@@ -113,7 +114,8 @@ const completeMasterpassPayment = async (
113
114
  ? await buildDirectPurchaseForm(commonFormValues, params)
114
115
  : await buildPurchaseForm({
115
116
  ...commonFormValues,
116
- selectedCard
117
+ selectedCard,
118
+ cardCvc: params?.card_cvv
117
119
  });
118
120
 
119
121
  window.MFS?.[
@@ -151,6 +153,10 @@ const completeMasterpassPayment = async (
151
153
  return;
152
154
  }
153
155
 
156
+ if (['5013', '5182'].includes(response.responseCode)) {
157
+ dispatch(setCvcRequired(true));
158
+ }
159
+
154
160
  if (
155
161
  response.token &&
156
162
  (response.responseCode == '0000' || response.responseCode == '')
@@ -373,6 +379,22 @@ export const checkoutApi = api.injectEndpoints({
373
379
  dispatch(setShippingStepBusy(false));
374
380
  }
375
381
  }),
382
+ setDataSourceShippingOptions: build.mutation<CheckoutResponse, number[]>({
383
+ query: (pks) => ({
384
+ url: buildClientRequestUrl(checkout.setDataSourceShippingOption, {
385
+ useFormData: true
386
+ }),
387
+ method: 'POST',
388
+ body: {
389
+ data_source_shipping_options: JSON.stringify(pks)
390
+ }
391
+ }),
392
+ async onQueryStarted(arg, { dispatch, queryFulfilled }) {
393
+ dispatch(setShippingStepBusy(true));
394
+ await queryFulfilled;
395
+ dispatch(setShippingStepBusy(false));
396
+ }
397
+ }),
376
398
  setRetailStore: build.mutation<CheckoutResponse, SetRetailStoreParams>({
377
399
  query: ({ retailStorePk, billingAddressPk }) => ({
378
400
  url: buildClientRequestUrl(
@@ -654,6 +676,25 @@ export const checkoutApi = api.injectEndpoints({
654
676
  notes
655
677
  }
656
678
  })
679
+ }),
680
+ setAttributeBasedShippingOptions: build.mutation<
681
+ CheckoutResponse,
682
+ Record<string, number>
683
+ >({
684
+ query: (options) => ({
685
+ url: buildClientRequestUrl(checkout.setAttributeBasedShippingOption, {
686
+ useFormData: true
687
+ }),
688
+ method: 'POST',
689
+ body: {
690
+ attribute_based_shipping_options: JSON.stringify(options)
691
+ }
692
+ }),
693
+ async onQueryStarted(arg, { dispatch, queryFulfilled }) {
694
+ dispatch(setShippingStepBusy(true));
695
+ await queryFulfilled;
696
+ dispatch(setShippingStepBusy(false));
697
+ }
657
698
  })
658
699
  }),
659
700
  overrideExisting: false
@@ -672,6 +713,7 @@ export const {
672
713
  useSetDeliveryOptionMutation,
673
714
  useSetAddressesMutation,
674
715
  useSetShippingOptionMutation,
716
+ useSetDataSourceShippingOptionsMutation,
675
717
  useSetPaymentOptionMutation,
676
718
  useSetBinNumberMutation,
677
719
  useSetInstallmentOptionMutation,
@@ -688,5 +730,6 @@ export const {
688
730
  useCompleteLoyaltyPaymentMutation,
689
731
  useGetCheckoutLoyaltyBalanceQuery,
690
732
  usePayWithLoyaltyBalanceMutation,
691
- useSetOrderNoteMutation
733
+ useSetOrderNoteMutation,
734
+ useSetAttributeBasedShippingOptionsMutation
692
735
  } = checkoutApi;
@@ -8,7 +8,7 @@ import logger from '../../utils/log';
8
8
 
9
9
  function getCategoryDataHandler(
10
10
  pk: number,
11
- searchParams?: URLSearchParams,
11
+ searchParams?: { [key: string]: string | string[] | undefined },
12
12
  headers?: Record<string, string>
13
13
  ) {
14
14
  return async function () {
@@ -78,7 +78,7 @@ export const getCategoryData = ({
78
78
  headers
79
79
  }: {
80
80
  pk: number;
81
- searchParams?: URLSearchParams;
81
+ searchParams?: { [key: string]: string | string[] | undefined };
82
82
  headers?: Record<string, string>;
83
83
  }) => {
84
84
  return Cache.wrap(
@@ -7,7 +7,7 @@ import { parse } from 'lossless-json';
7
7
  import logger from '../../utils/log';
8
8
 
9
9
  const getListDataHandler = (
10
- searchParams: URLSearchParams,
10
+ searchParams: { [key: string]: string | string[] | undefined },
11
11
  headers?: Record<string, string>
12
12
  ) => {
13
13
  return async function () {
@@ -54,7 +54,7 @@ export const getListData = async ({
54
54
  searchParams,
55
55
  headers
56
56
  }: {
57
- searchParams: URLSearchParams;
57
+ searchParams: { [key: string]: string | string[] | undefined };
58
58
  headers?: Record<string, string>;
59
59
  }) => {
60
60
  return Cache.wrap(
@@ -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?: URLSearchParams;
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 breadcrumbUrl = product.breadcrumbUrl(
54
- productCategoryData.results[0].menuitemmodel
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: URLSearchParams,
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: URLSearchParams;
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 = {
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: (searchParams: URLSearchParams, headers?: Record<string, string>) =>
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?: URLSearchParams,
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: URLSearchParams,
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: (pk: number, searchParams: URLSearchParams) =>
45
- `product_${pk}_${encodeURIComponent(JSON.stringify(searchParams))}`,
46
- GroupProduct: (pk: number, searchParams: URLSearchParams) =>
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,
@@ -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
- return NextResponse.rewrite(
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,