@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.
Files changed (53) hide show
  1. package/CHANGELOG.md +367 -0
  2. package/api/client.ts +39 -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/file-input.tsx +8 -0
  7. package/components/index.ts +1 -0
  8. package/components/input.tsx +21 -7
  9. package/components/link.tsx +17 -13
  10. package/components/pagination.tsx +1 -2
  11. package/components/price.tsx +11 -4
  12. package/components/selected-payment-option-view.tsx +26 -38
  13. package/data/client/account.ts +10 -9
  14. package/data/client/address.ts +32 -8
  15. package/data/client/api.ts +2 -2
  16. package/data/client/b2b.ts +35 -2
  17. package/data/client/basket.ts +6 -5
  18. package/data/client/checkout.ts +47 -4
  19. package/data/client/wishlist.ts +70 -2
  20. package/data/server/category.ts +2 -2
  21. package/data/server/list.ts +2 -2
  22. package/data/server/product.ts +15 -13
  23. package/data/server/special-page.ts +2 -2
  24. package/data/urls.ts +17 -3
  25. package/hooks/index.ts +2 -1
  26. package/hooks/use-message-listener.ts +24 -0
  27. package/hooks/use-payment-options.ts +2 -1
  28. package/lib/cache-handler.mjs +33 -0
  29. package/lib/cache.ts +18 -6
  30. package/middlewares/default.ts +50 -2
  31. package/middlewares/locale.ts +32 -30
  32. package/middlewares/pretty-url.ts +4 -0
  33. package/middlewares/url-redirection.ts +4 -0
  34. package/package.json +5 -4
  35. package/plugins.d.ts +1 -0
  36. package/redux/middlewares/checkout.ts +70 -11
  37. package/redux/reducers/checkout.ts +24 -5
  38. package/redux/reducers/config.ts +2 -0
  39. package/routes/pretty-url.tsx +194 -0
  40. package/types/commerce/account.ts +1 -0
  41. package/types/commerce/address.ts +1 -1
  42. package/types/commerce/b2b.ts +12 -2
  43. package/types/commerce/checkout.ts +30 -0
  44. package/types/commerce/misc.ts +2 -0
  45. package/types/commerce/order.ts +12 -0
  46. package/types/index.ts +30 -2
  47. package/utils/app-fetch.ts +1 -1
  48. package/utils/generate-commerce-search-params.ts +6 -2
  49. package/utils/index.ts +27 -6
  50. package/utils/menu-generator.ts +2 -2
  51. package/utils/redirection-iframe.ts +85 -0
  52. package/utils/server-translation.ts +5 -1
  53. package/with-pz-config.js +11 -1
@@ -10,28 +10,32 @@ type LinkProps = Omit<
10
10
  React.AnchorHTMLAttributes<HTMLAnchorElement>,
11
11
  keyof NextLinkProps
12
12
  > &
13
- NextLinkProps;
13
+ NextLinkProps & {
14
+ href: string;
15
+ };
14
16
 
15
17
  export const Link = ({ children, href, ...rest }: LinkProps) => {
16
18
  const { locale, defaultLocaleValue, localeUrlStrategy } = useLocalization();
17
19
  const formattedHref = useMemo(() => {
18
- if (typeof href !== 'string' || href.startsWith('http')) {
19
- return href;
20
+ if (!href) {
21
+ return '#';
20
22
  }
21
23
 
22
- const pathnameWithoutLocale = href.replace(urlLocaleMatcherRegex, '');
23
- const hrefWithLocale = `/${locale}${pathnameWithoutLocale}`;
24
+ if (typeof href === 'string' && !href.startsWith('http')) {
25
+ const pathnameWithoutLocale = href.replace(urlLocaleMatcherRegex, '');
26
+ const hrefWithLocale = `/${locale}${pathnameWithoutLocale}`;
24
27
 
25
- if (localeUrlStrategy === LocaleUrlStrategy.ShowAllLocales) {
26
- return hrefWithLocale;
27
- } else if (
28
- localeUrlStrategy === LocaleUrlStrategy.HideDefaultLocale &&
29
- locale !== defaultLocaleValue
30
- ) {
31
- return hrefWithLocale;
28
+ if (localeUrlStrategy === LocaleUrlStrategy.ShowAllLocales) {
29
+ return hrefWithLocale;
30
+ } else if (
31
+ localeUrlStrategy === LocaleUrlStrategy.HideDefaultLocale &&
32
+ locale !== defaultLocaleValue
33
+ ) {
34
+ return hrefWithLocale;
35
+ }
32
36
  }
33
37
 
34
- return href || '#';
38
+ return href;
35
39
  }, [href, defaultLocaleValue, locale, localeUrlStrategy]);
36
40
 
37
41
  return (
@@ -1,10 +1,9 @@
1
1
  'use client';
2
2
 
3
3
  import { MouseEvent, useCallback, useEffect, useState } from 'react';
4
- import { PaginationProps } from '@theme/components/types';
4
+ import { PaginationProps } from '../types';
5
5
  import { twMerge } from 'tailwind-merge';
6
6
  import clsx from 'clsx';
7
-
8
7
  import usePagination from '@akinon/next/hooks/use-pagination';
9
8
  import { useLocalization } from '@akinon/next/hooks';
10
9
  import { useRouter } from '@akinon/next/hooks';
@@ -1,11 +1,14 @@
1
1
  import { useMemo } from 'react';
2
- import NumberFormat, { NumberFormatProps } from 'react-number-format';
2
+
3
+ // @ts-ignore
4
+ import { NumericFormat, NumericFormatProps } from 'react-number-format';
3
5
  import { getCurrency } from '@akinon/next/utils';
4
6
 
5
7
  import { useLocalization } from '@akinon/next/hooks';
6
8
  import { PriceProps } from '../types';
9
+ import Settings from 'settings';
7
10
 
8
- export const Price = (props: NumberFormatProps & PriceProps) => {
11
+ export const Price = (props: NumericFormatProps & PriceProps) => {
9
12
  const {
10
13
  value,
11
14
  currencyCode,
@@ -27,6 +30,10 @@ export const Price = (props: NumberFormatProps & PriceProps) => {
27
30
  // TODO: This is very bad practice. It broke decimalScale.
28
31
  const _value = value?.toString().replace('.', ',');
29
32
 
33
+ const currentCurrencyDecimalScale = Settings.localization.currencies.find(
34
+ (currency) => currency.code === currencyCode_
35
+ ).decimalScale;
36
+
30
37
  const currency = useMemo(
31
38
  () =>
32
39
  getCurrency({
@@ -39,14 +46,14 @@ export const Price = (props: NumberFormatProps & PriceProps) => {
39
46
  );
40
47
 
41
48
  return (
42
- <NumberFormat
49
+ <NumericFormat
43
50
  value={useNegative ? `-${useNegativeSpace}${_value}` : _value}
44
51
  {...{
45
52
  [useCurrencyAfterPrice ? 'suffix' : 'prefix']: currency
46
53
  }}
47
54
  displayType={displayType}
48
55
  thousandSeparator={thousandSeparator}
49
- decimalScale={decimalScale}
56
+ decimalScale={currentCurrencyDecimalScale ?? decimalScale}
50
57
  decimalSeparator={decimalSeparator}
51
58
  fixedDecimalScale={fixedDecimalScale}
52
59
  {...rest}
@@ -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;
@@ -67,7 +67,7 @@ export const api = createApi({
67
67
  baseQuery: customBaseQuery,
68
68
  tagTypes: [
69
69
  'Basket',
70
- 'AllBaskets',
70
+ 'MultiBasket',
71
71
  'BasketB2b',
72
72
  'DraftsB2b',
73
73
  'Product',
@@ -12,7 +12,9 @@ import {
12
12
  SaveBasketParams,
13
13
  UpdateProductParams,
14
14
  DeleteProductParams,
15
- CreateQuotationParams
15
+ CreateQuotationParams,
16
+ BasketStatusResponse,
17
+ ExportBasketResponse
16
18
  } from '../../types';
17
19
 
18
20
  const b2bApi = api.injectEndpoints({
@@ -89,6 +91,34 @@ const b2bApi = api.injectEndpoints({
89
91
  }),
90
92
  invalidatesTags: ['BasketB2b', 'DraftsB2b']
91
93
  }),
94
+ exportBasket: build.mutation<ExportBasketResponse, string>({
95
+ query: (queryString) => {
96
+ return {
97
+ url: buildClientRequestUrl(b2b.basketExport(queryString)),
98
+ method: 'GET'
99
+ };
100
+ }
101
+ }),
102
+ getBasketStatus: build.mutation<BasketStatusResponse, string>({
103
+ query: (cacheKey) => {
104
+ return {
105
+ url: buildClientRequestUrl(b2b.statusBasket(cacheKey)),
106
+ method: 'GET'
107
+ };
108
+ }
109
+ }),
110
+ uploadFile: build.mutation<void, FormData>({
111
+ query: (body) => {
112
+ return {
113
+ url: buildClientRequestUrl(b2b.basketImport, {
114
+ useFormData: true
115
+ }),
116
+ method: 'POST',
117
+ body
118
+ };
119
+ },
120
+ invalidatesTags: ['BasketB2b']
121
+ })
92
122
  }),
93
123
  overrideExisting: true
94
124
  });
@@ -102,5 +132,8 @@ export const {
102
132
  useLoadBasketMutation,
103
133
  useUpdateProductMutation,
104
134
  useDeleteProductMutation,
105
- useCreateQuotationMutation
135
+ useCreateQuotationMutation,
136
+ useGetBasketStatusMutation,
137
+ useExportBasketMutation,
138
+ useUploadFileMutation
106
139
  } = b2bApi;
@@ -28,7 +28,7 @@ export const basketApi = api.injectEndpoints({
28
28
  query: ({ namespace }) =>
29
29
  buildClientRequestUrl(basket.getBasketDetail(namespace)),
30
30
  transformResponse: (response: { basket: Basket }) => response.basket,
31
- providesTags: ['AllBaskets']
31
+ providesTags: ['MultiBasket']
32
32
  }),
33
33
  getAllBaskets: build.query<Basket[], void>({
34
34
  query: () =>
@@ -36,7 +36,7 @@ export const basketApi = api.injectEndpoints({
36
36
  contentType: 'application/json'
37
37
  }),
38
38
  transformResponse: (response: { baskets: Basket[] }) => response.baskets,
39
- providesTags: ['AllBaskets']
39
+ providesTags: ['MultiBasket']
40
40
  }),
41
41
  removeBasket: build.mutation<Basket, { pk: number }>({
42
42
  query: ({ pk }) => ({
@@ -46,7 +46,7 @@ export const basketApi = api.injectEndpoints({
46
46
  method: 'DELETE',
47
47
  body: { pk }
48
48
  }),
49
- invalidatesTags: ['AllBaskets', 'Basket']
49
+ invalidatesTags: ['MultiBasket', 'Basket']
50
50
  }),
51
51
  selectMainBasket: build.mutation<Basket, { pk: number }>({
52
52
  query: ({ pk }) => ({
@@ -57,7 +57,7 @@ export const basketApi = api.injectEndpoints({
57
57
  body: { pk }
58
58
  }),
59
59
  transformResponse: (response: { baskets: Basket }) => response.baskets,
60
- invalidatesTags: ['AllBaskets', 'Basket']
60
+ invalidatesTags: ['MultiBasket', 'Basket']
61
61
  }),
62
62
  updateQuantity: build.mutation<
63
63
  UpdateQuantityResponse,
@@ -69,7 +69,8 @@ export const basketApi = api.injectEndpoints({
69
69
  }),
70
70
  method: 'PUT',
71
71
  body
72
- })
72
+ }),
73
+ invalidatesTags: ['MultiBasket', 'Basket']
73
74
  }),
74
75
  clearBasket: build.mutation<Basket, void>({
75
76
  query: (body) => ({
@@ -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;
@@ -1,4 +1,4 @@
1
- import { FavoriteItem } from '../../types';
1
+ import { FavoriteItem, Product } from '../../types';
2
2
  import { buildClientRequestUrl } from '../../utils';
3
3
  import { api } from './api';
4
4
  import { wishlist } from '../urls';
@@ -55,6 +55,25 @@ interface GetStockAlertsResponse {
55
55
  interface DeleteStockAlertResponse {
56
56
  success: boolean;
57
57
  }
58
+ interface GetCollectionOOSResponse {
59
+ count: number;
60
+ results: CollectionItem[];
61
+ next?: string | null;
62
+ previous?: string | null;
63
+ }
64
+
65
+ export interface CollectionItem {
66
+ pk: number;
67
+ status: string;
68
+ name: string;
69
+ slug: string;
70
+ items: Product[];
71
+ }
72
+
73
+ export interface CollectionItemsResponse {
74
+ pk: number;
75
+ product: Product;
76
+ }
58
77
 
59
78
  export const wishlistApi = api.injectEndpoints({
60
79
  endpoints: (build) => ({
@@ -105,6 +124,50 @@ export const wishlistApi = api.injectEndpoints({
105
124
  method: 'DELETE'
106
125
  }),
107
126
  invalidatesTags: ['StockAlert']
127
+ }),
128
+ setCollectionOOS: build.mutation<
129
+ CollectionItem,
130
+ { name: string; status: string }
131
+ >({
132
+ query: ({ name, status = 'private' }) => ({
133
+ url: buildClientRequestUrl(wishlist.setCollection(), {
134
+ contentType: 'application/json'
135
+ }),
136
+ method: 'POST',
137
+ body: {
138
+ name,
139
+ status
140
+ }
141
+ })
142
+ }),
143
+ setCollectionItems: build.mutation<
144
+ CollectionItemsResponse,
145
+ { usercollection_id: number; product_id: number | number[] }
146
+ >({
147
+ query: ({ usercollection_id, product_id }) => ({
148
+ url: buildClientRequestUrl(wishlist.setCollectionItems, {
149
+ contentType: 'application/json'
150
+ }),
151
+ method: 'POST',
152
+ body: {
153
+ usercollection_id,
154
+ product_id
155
+ }
156
+ })
157
+ }),
158
+ getCollectionsOOS: build.query<
159
+ GetCollectionOOSResponse,
160
+ { search?: string; product_id?: number }
161
+ >({
162
+ query: ({ search, product_id }) => ({
163
+ url: buildClientRequestUrl(wishlist.getCollections(search, product_id))
164
+ })
165
+ }),
166
+ deleteCollection: build.query<void, number>({
167
+ query: (pk: number) => ({
168
+ url: buildClientRequestUrl(wishlist.deleteCollection(pk)),
169
+ method: 'DELETE'
170
+ })
108
171
  })
109
172
  }),
110
173
  overrideExisting: true
@@ -116,5 +179,10 @@ export const {
116
179
  useRemoveFavoriteMutation,
117
180
  useAddStockAlertMutation,
118
181
  useGetStockAlertsQuery,
119
- useDeleteStockAlertMutation
182
+ useDeleteStockAlertMutation,
183
+ useSetCollectionOOSMutation,
184
+ useSetCollectionItemsMutation,
185
+ useGetCollectionsOOSQuery,
186
+ useLazyGetCollectionsOOSQuery,
187
+ useLazyDeleteCollectionQuery
120
188
  } = wishlistApi;
@@ -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(