@akinon/next 1.0.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 (98) hide show
  1. package/.prettierrc +13 -0
  2. package/api/auth.ts +217 -0
  3. package/api/cache.ts +44 -0
  4. package/api/client.ts +157 -0
  5. package/api/logout.ts +42 -0
  6. package/assets/styles/index.scss +24 -0
  7. package/bin/pz-install-plugins.js +33 -0
  8. package/components/client-root.tsx +69 -0
  9. package/components/image.tsx +133 -0
  10. package/components/mobile-app-toggler.tsx +15 -0
  11. package/components/oauth-login.tsx +24 -0
  12. package/components/plugin-module.tsx +78 -0
  13. package/components/pz-providers.tsx +24 -0
  14. package/components/pz-root.tsx +21 -0
  15. package/components/redirect-three-d/content/index.tsx +64 -0
  16. package/components/redirect-three-d/index.tsx +17 -0
  17. package/components/selected-payment-option-view.tsx +66 -0
  18. package/components/trans.tsx +39 -0
  19. package/data/client/account.ts +188 -0
  20. package/data/client/address.ts +107 -0
  21. package/data/client/api.ts +42 -0
  22. package/data/client/basket.ts +85 -0
  23. package/data/client/checkout.ts +477 -0
  24. package/data/client/misc.ts +101 -0
  25. package/data/client/product.ts +90 -0
  26. package/data/client/user.ts +83 -0
  27. package/data/client/wishlist.ts +79 -0
  28. package/data/server/category.ts +121 -0
  29. package/data/server/flatpage.ts +21 -0
  30. package/data/server/index.ts +8 -0
  31. package/data/server/list.ts +56 -0
  32. package/data/server/menu.ts +35 -0
  33. package/data/server/product.ts +86 -0
  34. package/data/server/seo.ts +48 -0
  35. package/data/server/special-page.ts +42 -0
  36. package/data/server/widget.ts +27 -0
  37. package/data/urls.ts +184 -0
  38. package/hocs/client/index.ts +1 -0
  39. package/hocs/client/with-segment-defaults.tsx +26 -0
  40. package/hocs/server/index.ts +1 -0
  41. package/hocs/server/with-segment-defaults.tsx +83 -0
  42. package/hooks/index.ts +8 -0
  43. package/hooks/use-captcha.tsx +76 -0
  44. package/hooks/use-common-product-attributes.ts +36 -0
  45. package/hooks/use-debounce.ts +20 -0
  46. package/hooks/use-localization.ts +63 -0
  47. package/hooks/use-media-query.ts +36 -0
  48. package/hooks/use-on-click-outside.tsx +28 -0
  49. package/hooks/use-router.ts +45 -0
  50. package/hooks/use-translation.ts +14 -0
  51. package/lib/cache.ts +185 -0
  52. package/localization/index.ts +5 -0
  53. package/localization/provider.tsx +58 -0
  54. package/middlewares/currency.ts +55 -0
  55. package/middlewares/default.ts +224 -0
  56. package/middlewares/index.ts +29 -0
  57. package/middlewares/locale.ts +61 -0
  58. package/middlewares/oauth-login.ts +78 -0
  59. package/middlewares/pretty-url.ts +94 -0
  60. package/middlewares/redirection-payment.ts +117 -0
  61. package/middlewares/three-d-redirection.ts +122 -0
  62. package/middlewares/url-redirection.ts +61 -0
  63. package/package.json +20 -0
  64. package/plugins.js +7 -0
  65. package/redux/hooks.ts +7 -0
  66. package/redux/middlewares/checkout.ts +231 -0
  67. package/redux/middlewares/index.ts +50 -0
  68. package/redux/reducers/checkout.ts +164 -0
  69. package/redux/reducers/config.ts +28 -0
  70. package/redux/reducers/header.ts +59 -0
  71. package/redux/reducers/index.ts +15 -0
  72. package/redux/reducers/root.ts +61 -0
  73. package/tailwind/rtl.js +137 -0
  74. package/types/commerce/account.ts +68 -0
  75. package/types/commerce/address.ts +94 -0
  76. package/types/commerce/basket.ts +43 -0
  77. package/types/commerce/category.ts +114 -0
  78. package/types/commerce/checkout.ts +132 -0
  79. package/types/commerce/flatpage.ts +7 -0
  80. package/types/commerce/index.ts +10 -0
  81. package/types/commerce/misc.ts +127 -0
  82. package/types/commerce/order.ts +108 -0
  83. package/types/commerce/product.ts +110 -0
  84. package/types/commerce/widget.ts +28 -0
  85. package/types/gtm.ts +16 -0
  86. package/types/index.ts +207 -0
  87. package/types/next-auth.d.ts +24 -0
  88. package/utils/app-fetch.ts +62 -0
  89. package/utils/generate-commerce-search-params.ts +22 -0
  90. package/utils/get-currency.ts +29 -0
  91. package/utils/image-loader.ts +31 -0
  92. package/utils/index.ts +132 -0
  93. package/utils/localization.ts +29 -0
  94. package/utils/log.ts +138 -0
  95. package/utils/menu-generator.ts +27 -0
  96. package/utils/mobile-3d-iframe.ts +58 -0
  97. package/utils/server-translation.ts +57 -0
  98. package/utils/server-variables.ts +9 -0
@@ -0,0 +1,133 @@
1
+ 'use client';
2
+
3
+ import { CDNOptions } from '../types';
4
+ import NextImage, { ImageProps as NextImageProps } from 'next/image';
5
+ import { twMerge } from 'tailwind-merge';
6
+ import { ImageLoader } from '../utils/image-loader';
7
+
8
+ interface ImageProps extends NextImageProps {
9
+ /**
10
+ * Use fill to make the image responsive.
11
+ * You also need to provide sizes and aspectRatio props.
12
+ *
13
+ * @default false
14
+ */
15
+ fill?: boolean;
16
+ /**
17
+ * Crop option for CDN
18
+ *
19
+ * Available options:
20
+ * - `center`
21
+ * - `top`
22
+ * - `bottom`
23
+ * - `left`
24
+ * - `right`
25
+ *
26
+ * @default 'center'
27
+ */
28
+ crop?: CDNOptions['crop'];
29
+ /**
30
+ * It is used to calculate the height of the image when fill is true.
31
+ * It prevents layout shift problems.
32
+ */
33
+ aspectRatio?: number;
34
+ /**
35
+ * Sizes attribute for responsive images
36
+ *
37
+ * If it is not provided, large images will be downloaded for small screens and it will cause performance issues.
38
+ *
39
+ * @example
40
+ * <Image
41
+ * src={src}
42
+ * sizes="(min-width: 768px) 50vw, 100vw"
43
+ * fill
44
+ * />
45
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-sizes
46
+ */
47
+ sizes?: string;
48
+ imageClassName?: string;
49
+ }
50
+
51
+ const DEFAULT_QUALITY = 70;
52
+
53
+ /**
54
+ * Image component for responsive images.
55
+ *
56
+ * It uses CDN options automatically if the image is hosted on Akinon CDN. You don't need to convert the src prop to CDN url.
57
+ *
58
+ * ## For responsive images
59
+ * You need to provide `fill`, `sizes` and `aspectRatio` props.
60
+ * If you are not sure about sizes, you can give `100vw` as a value. But it may cause performance issues.
61
+ *
62
+ * ## For non-responsive images
63
+ * You need to provide `width` and `height` props.
64
+ * This way, the browser will know the size of the image before downloading it and it will prevent layout shift problems.
65
+ *
66
+ * ## CDN options
67
+ * You can use `crop` option to crop the image.
68
+ *
69
+ * Available options:
70
+ * - `center`
71
+ * - `top`
72
+ * - `bottom`
73
+ * - `left`
74
+ * - `right`
75
+ *
76
+ * You can also use `quality` option to change the quality of the image.
77
+ * Be careful about the file size when you change the quality.
78
+ *
79
+ * @default 70
80
+ */
81
+ export const Image = (props: ImageProps) => {
82
+ const {
83
+ src,
84
+ width,
85
+ fill,
86
+ sizes,
87
+ aspectRatio,
88
+ quality = DEFAULT_QUALITY,
89
+ crop = 'center',
90
+ className,
91
+ imageClassName,
92
+ ...restImage
93
+ } = props;
94
+
95
+ const hasGif = typeof src === 'string' && src.includes('.gif');
96
+
97
+ if (fill && !aspectRatio) {
98
+ throw new Error('aspectRatio is required when fill is true');
99
+ }
100
+
101
+ if (fill && !sizes) {
102
+ throw new Error('sizes is required when fill is true');
103
+ }
104
+
105
+ return (
106
+ <div
107
+ className={twMerge('relative inline-block', fill && 'w-full', className)}
108
+ style={{
109
+ ...(fill && { aspectRatio })
110
+ }}
111
+ >
112
+ <NextImage
113
+ {...restImage}
114
+ width={width}
115
+ src={src}
116
+ sizes={sizes}
117
+ fill={fill}
118
+ loader={({ src, width }) => {
119
+ return ImageLoader({
120
+ src,
121
+ width,
122
+ quality: parseInt(String(quality), DEFAULT_QUALITY),
123
+ crop,
124
+ fill,
125
+ aspectRatio
126
+ });
127
+ }}
128
+ className={imageClassName}
129
+ {...(hasGif && { unoptimized: true })}
130
+ />
131
+ </div>
132
+ );
133
+ };
@@ -0,0 +1,15 @@
1
+ 'use client';
2
+
3
+ import { useAppSelector } from '../redux/hooks';
4
+
5
+ export default function MobileAppToggler({
6
+ children
7
+ }: {
8
+ children: React.ReactNode;
9
+ }) {
10
+ const { isMobileApp } = useAppSelector((state) => state.root);
11
+
12
+ if (isMobileApp) return null;
13
+
14
+ return <>{children}</>;
15
+ }
@@ -0,0 +1,24 @@
1
+ 'use client';
2
+
3
+ import { signIn, useSession } from 'next-auth/react';
4
+ import { useEffect } from 'react';
5
+ import { useSearchParams } from 'next/navigation';
6
+
7
+ export default function OauthLogin() {
8
+ const { status } = useSession();
9
+ const searchParams = useSearchParams();
10
+
11
+ useEffect(() => {
12
+ if (!searchParams) {
13
+ return;
14
+ }
15
+
16
+ if (status === 'unauthenticated') {
17
+ signIn('oauth', {
18
+ callbackUrl: searchParams.get('next') ?? '/'
19
+ });
20
+ }
21
+ }, [status, searchParams]);
22
+
23
+ return null;
24
+ }
@@ -0,0 +1,78 @@
1
+ import dynamic from 'next/dynamic';
2
+ import plugins from 'plugins';
3
+ import logger from '../utils/log';
4
+
5
+ enum Plugin {
6
+ BasketGiftPack = 'pz-basket-gift-pack',
7
+ ClickCollect = 'pz-click-collect',
8
+ OneClickCheckout = 'pz-one-click-checkout',
9
+ PayOnDelivery = 'pz-pay-on-delivery',
10
+ CheckoutGiftPack = 'pz-checkout-gift-pack'
11
+ }
12
+
13
+ export enum Component {
14
+ BasketGiftPack = 'BasketGiftPack',
15
+ ClickCollect = 'ClickCollect',
16
+ OneClickCheckoutButtons = 'OneClickCheckoutButtons',
17
+ PayOnDelivery = 'PayOnDelivery',
18
+ CheckoutGiftPack = 'CheckoutGiftPack'
19
+ }
20
+
21
+ const PluginComponents = new Map([
22
+ [Plugin.BasketGiftPack, [Component.BasketGiftPack]],
23
+ [Plugin.ClickCollect, [Component.ClickCollect]],
24
+ [Plugin.OneClickCheckout, [Component.OneClickCheckoutButtons]],
25
+ [Plugin.PayOnDelivery, [Component.PayOnDelivery]],
26
+ [Plugin.CheckoutGiftPack, [Component.CheckoutGiftPack]]
27
+ ]);
28
+
29
+ const getPlugin = (component: Component) => {
30
+ for (let [key, value] of Array.from(PluginComponents.entries())) {
31
+ if (value.includes(component)) return key;
32
+ }
33
+
34
+ return '';
35
+ };
36
+
37
+ export default function PluginModule({
38
+ component,
39
+ props
40
+ }: {
41
+ component: Component;
42
+ props?: any;
43
+ }) {
44
+ const plugin = getPlugin(component);
45
+
46
+ if (!(plugins as string[]).includes(plugin)) {
47
+ return null;
48
+ }
49
+
50
+ const Component = dynamic(
51
+ () => {
52
+ let promise: any;
53
+
54
+ try {
55
+ if (plugin === Plugin.BasketGiftPack) {
56
+ promise = import(`${'pz-basket-gift-pack'}`);
57
+ } else if (plugin === Plugin.ClickCollect) {
58
+ promise = import(`${'pz-click-collect'}`);
59
+ } else if (plugin === Plugin.OneClickCheckout) {
60
+ promise = import(`${'pz-one-click-checkout'}`);
61
+ } else if (plugin === Plugin.PayOnDelivery) {
62
+ promise = import(`${'pz-pay-on-delivery'}`);
63
+ } else if (plugin === Plugin.CheckoutGiftPack) {
64
+ promise = import(`${'pz-checkout-gift-pack'}`);
65
+ }
66
+ } catch (error) {
67
+ logger.error(error);
68
+ }
69
+
70
+ return promise
71
+ ? promise.then((mod) => mod[component]).catch(() => () => null)
72
+ : new Promise<any>((resolve) => resolve(() => null));
73
+ },
74
+ { ssr: false }
75
+ );
76
+
77
+ return <Component {...props} />;
78
+ }
@@ -0,0 +1,24 @@
1
+ 'use client';
2
+
3
+ import LocalizationProvider from '@akinon/next/localization/provider';
4
+ import { SessionProvider } from 'next-auth/react';
5
+ import { Provider } from 'react-redux';
6
+ import { store } from 'redux/store';
7
+
8
+ export default function PzProviders({
9
+ translations,
10
+ children
11
+ }: {
12
+ translations: any;
13
+ children: React.ReactNode;
14
+ }) {
15
+ return (
16
+ <Provider store={store}>
17
+ <SessionProvider refetchOnWindowFocus={false}>
18
+ <LocalizationProvider translations={translations}>
19
+ {children}
20
+ </LocalizationProvider>
21
+ </SessionProvider>
22
+ </Provider>
23
+ );
24
+ }
@@ -0,0 +1,21 @@
1
+ import 'server-only';
2
+ import ClientRoot from './client-root';
3
+ import { cookies } from 'next/headers';
4
+ import PzProviders from './pz-providers';
5
+
6
+ export default function PzRoot({
7
+ translations,
8
+ children
9
+ }: {
10
+ translations: any;
11
+ children: React.ReactNode;
12
+ }) {
13
+ const nextCookies = cookies();
14
+ const sessionid = nextCookies.get('osessionid')?.value;
15
+
16
+ return (
17
+ <PzProviders translations={translations}>
18
+ <ClientRoot sessionId={sessionid}>{children}</ClientRoot>
19
+ </PzProviders>
20
+ );
21
+ }
@@ -0,0 +1,64 @@
1
+ 'use client';
2
+
3
+ import { useRouter } from '@akinon/next/hooks';
4
+ import { useEffect, useState } from 'react';
5
+ import { ROUTES } from 'routes';
6
+ import { useGet3dRedirectFormQuery } from '@akinon/next/data/client/checkout';
7
+ import { LoaderSpinner } from 'components';
8
+ import { useLocalization } from '../../../hooks/use-localization';
9
+
10
+ interface RedirectThreeDContentProps {
11
+ sessionId: string;
12
+ }
13
+
14
+ export default function RedirectThreeDContent({
15
+ sessionId
16
+ }: RedirectThreeDContentProps) {
17
+ const { data } = useGet3dRedirectFormQuery();
18
+ const [error, setError] = useState(null);
19
+ const router = useRouter();
20
+ const { locale } = useLocalization();
21
+
22
+ useEffect(() => {
23
+ if (data) {
24
+ const fragment = document.createElement('fragment');
25
+ fragment.innerHTML = data.result;
26
+
27
+ const form = fragment.querySelector('form');
28
+
29
+ // a way to determine if response includes a redirection form or not
30
+ if (fragment.querySelector('link[rel="canonical"]') || !form) {
31
+ setError('Redirecting to checkout page. Please wait...');
32
+
33
+ setTimeout(() => {
34
+ router.replace(ROUTES.CHECKOUT);
35
+ }, 3000);
36
+ return;
37
+ }
38
+
39
+ const pzParamsInput = document.createElement('input');
40
+ pzParamsInput.setAttribute('type', 'hidden');
41
+ pzParamsInput.setAttribute('name', 'pzparams');
42
+ pzParamsInput.setAttribute(
43
+ 'value',
44
+ encodeURIComponent(
45
+ JSON.stringify({
46
+ session: sessionId,
47
+ locale
48
+ })
49
+ )
50
+ );
51
+ form.appendChild(pzParamsInput);
52
+
53
+ form.style.display = 'none';
54
+ document.body.appendChild(form);
55
+ form.submit();
56
+ }
57
+ }, [data]);
58
+
59
+ return (
60
+ <div className="flex items-center justify-center py-20">
61
+ {error ?? <LoaderSpinner />}
62
+ </div>
63
+ );
64
+ }
@@ -0,0 +1,17 @@
1
+ import { cookies } from 'next/headers';
2
+ import { redirect } from 'next/navigation';
3
+ import RedirectThreeDContent from './content';
4
+ import { ROUTES } from 'routes';
5
+
6
+ const RedirectThreeD = async () => {
7
+ const nextCookies = cookies();
8
+ const sessionId = await nextCookies.get('osessionid')?.value;
9
+
10
+ if (!sessionId) {
11
+ return redirect(ROUTES.CHECKOUT);
12
+ }
13
+
14
+ return <RedirectThreeDContent sessionId={sessionId} />;
15
+ };
16
+
17
+ export default RedirectThreeD;
@@ -0,0 +1,66 @@
1
+ 'use client';
2
+
3
+ import { RootState } from 'redux/store';
4
+ import { useAppSelector } from '../redux/hooks';
5
+ import dynamic from 'next/dynamic';
6
+ import { PaymentOptionViews } from 'views/checkout/steps/payment';
7
+ import { useMemo } from 'react';
8
+
9
+ export default function SelectedPaymentOptionView() {
10
+ const { payment_option } = useAppSelector(
11
+ (state: RootState) => state.checkout.preOrder
12
+ );
13
+
14
+ const Component = useMemo(
15
+ () =>
16
+ dynamic(
17
+ () => {
18
+ const customOption = PaymentOptionViews.find(
19
+ (opt) => opt.pk == payment_option.pk
20
+ );
21
+
22
+ if (customOption) {
23
+ const Component = customOption.view;
24
+
25
+ const componentWrapper = () => <Component />;
26
+
27
+ return new Promise((resolve) => {
28
+ resolve(componentWrapper);
29
+ });
30
+ }
31
+
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
+ }
54
+ } catch (error) {}
55
+
56
+ return promise
57
+ ? promise.then((mod) => mod.default).catch(() => () => null)
58
+ : new Promise<any>((resolve) => resolve(() => null));
59
+ },
60
+ { ssr: false }
61
+ ),
62
+ [payment_option?.pk]
63
+ );
64
+
65
+ return <Component />;
66
+ }
@@ -0,0 +1,39 @@
1
+ 'use client';
2
+
3
+ import { Fragment, useMemo } from 'react';
4
+ import reactStringReplace from 'react-string-replace';
5
+ import { useLocalization } from '@akinon/next/hooks';
6
+
7
+ interface TransProps {
8
+ i18nKey: string;
9
+ components?: {
10
+ [key: string]: React.ReactElement;
11
+ };
12
+ }
13
+
14
+ export const Trans = ({ i18nKey, components }: TransProps) => {
15
+ const { t } = useLocalization();
16
+ const value = t(i18nKey);
17
+
18
+ const replacedValue = useMemo(() => {
19
+ if (!components) {
20
+ return value;
21
+ }
22
+
23
+ let text;
24
+
25
+ for (const [k] of Object.entries(components)) {
26
+ text = reactStringReplace(
27
+ text ?? value,
28
+ new RegExp(`(<${k}/>|<${k} />)`),
29
+ (_match, i) => (
30
+ <Fragment key={`trans-${k}-${i}`}>{components[k]}</Fragment>
31
+ )
32
+ );
33
+ }
34
+
35
+ return text;
36
+ }, [value, components]);
37
+
38
+ return <>{replacedValue}</>;
39
+ };
@@ -0,0 +1,188 @@
1
+ import { api } from './api';
2
+ import { account, user } from '../urls';
3
+ import { buildClientRequestUrl } from '../../utils';
4
+ import {
5
+ AccountChangeEmailFormType,
6
+ AccountChangePasswordFormType,
7
+ AccountOrderCancellation,
8
+ AccountOrderCancellationReason,
9
+ ContactFormType,
10
+ Order
11
+ } from '../../types';
12
+
13
+ interface GetOrdersResponse {
14
+ count: number;
15
+ results: Order[];
16
+ }
17
+
18
+ interface GetOrdersParams {
19
+ limit?: number;
20
+ page?: number;
21
+ createdDate?: string;
22
+ }
23
+
24
+ export type PasswordResetType = {
25
+ password: string;
26
+ repeatPassword: string;
27
+ slug: string;
28
+ };
29
+
30
+ export type AccountProfileFormFilteredType = {
31
+ first_name: string;
32
+ last_name: string;
33
+ phone: string;
34
+ date_of_birth: string;
35
+ gender: string;
36
+ sms_allowed: boolean;
37
+ email_allowed: boolean;
38
+ };
39
+
40
+ export type ContactFormSubject = [
41
+ {
42
+ id: string;
43
+ is_order_needed: boolean;
44
+ text: string;
45
+ }
46
+ ];
47
+
48
+ interface LoyaltyTransactions {
49
+ count: number;
50
+ results: {
51
+ amount: string;
52
+ created_date: string;
53
+ transaction: {
54
+ created_date: string;
55
+ order: string;
56
+ };
57
+ }[];
58
+ }
59
+
60
+ const accountApi = api.injectEndpoints({
61
+ endpoints: (builder) => ({
62
+ updatePassword: builder.mutation<void, AccountChangePasswordFormType>({
63
+ query: (body) => ({
64
+ url: buildClientRequestUrl(account.updatePassword, {
65
+ contentType: 'application/json'
66
+ }),
67
+ method: 'POST',
68
+ body
69
+ })
70
+ }),
71
+ updateEmail: builder.mutation<void, AccountChangeEmailFormType>({
72
+ query: (body) => ({
73
+ url: buildClientRequestUrl(account.updateEmail, {
74
+ contentType: 'application/json'
75
+ }),
76
+ method: 'POST',
77
+ body
78
+ })
79
+ }),
80
+ updateProfile: builder.mutation<void, AccountProfileFormFilteredType>({
81
+ query: (body) => ({
82
+ url: buildClientRequestUrl(account.updateProfile, {
83
+ accept: 'application/json',
84
+ contentType: 'application/json'
85
+ }),
86
+ method: 'PATCH',
87
+ body
88
+ }),
89
+ invalidatesTags: ['Profile']
90
+ }),
91
+ getProfileInfo: builder.query<any, void>({
92
+ query: () => buildClientRequestUrl(user.profiles),
93
+ providesTags: ['Profile']
94
+ }),
95
+ getContactSubjects: builder.query<ContactFormSubject, void>({
96
+ query: () => buildClientRequestUrl(account.getContactSubjects)
97
+ }),
98
+ getOrder: builder.query<any, string>({
99
+ query: (id) => buildClientRequestUrl(account.orderId(id))
100
+ }),
101
+ getOrders: builder.query<GetOrdersResponse, GetOrdersParams>({
102
+ query: ({ page, limit, createdDate } = {}) =>
103
+ buildClientRequestUrl(account.getOrders({ page, limit, createdDate }))
104
+ }),
105
+ sendContact: builder.mutation<void, ContactFormType>({
106
+ query: (body) => ({
107
+ url: buildClientRequestUrl(account.sendContact, {
108
+ contentType: 'application/json',
109
+ responseType: 'text'
110
+ }),
111
+ method: 'POST',
112
+ body
113
+ })
114
+ }),
115
+ cancelOrder: builder.mutation<void, AccountOrderCancellation>({
116
+ query: ({ id, ...body }) => ({
117
+ url: buildClientRequestUrl(account.cancelOrder(id), {
118
+ contentType: 'application/json'
119
+ }),
120
+ method: 'POST',
121
+ body
122
+ })
123
+ }),
124
+ getCancellationReasons: builder.query<AccountOrderCancellationReason, void>(
125
+ {
126
+ query: () => buildClientRequestUrl(account.cancellationReasons)
127
+ }
128
+ ),
129
+ passwordReset: builder.mutation<void, PasswordResetType>({
130
+ query: ({ slug, password, repeatPassword }) => ({
131
+ url: buildClientRequestUrl(account.passwordReset(slug), {
132
+ useFormData: true
133
+ }),
134
+ method: 'POST',
135
+ body: {
136
+ new_password1: password,
137
+ new_password2: repeatPassword
138
+ }
139
+ })
140
+ }),
141
+ getBasketOffers: builder.query<any, void>({
142
+ query: () => buildClientRequestUrl('/account/basket-offers')
143
+ }),
144
+ getFutureBasketOffers: builder.query<any, void>({
145
+ query: () => buildClientRequestUrl('/account/future-basket-offers')
146
+ }),
147
+ getExpiredBasketOffers: builder.query<any, void>({
148
+ query: () => buildClientRequestUrl('/account/expired-basket-offers')
149
+ }),
150
+ getDiscountItems: builder.query<any, void>({
151
+ query: () => buildClientRequestUrl('/account/discount-items')
152
+ }),
153
+ anonymize: builder.mutation<string, void>({
154
+ query: () => ({
155
+ url: buildClientRequestUrl(account.anonymize),
156
+ method: 'PATCH'
157
+ })
158
+ }),
159
+ getLoyaltyBalance: builder.query<{ balance: number }, void>({
160
+ query: () => buildClientRequestUrl(account.loyaltyBalance)
161
+ }),
162
+ getLoyaltyTransactions: builder.query<LoyaltyTransactions, void>({
163
+ query: () => buildClientRequestUrl(account.loyaltyTransactions)
164
+ })
165
+ }),
166
+ overrideExisting: true
167
+ });
168
+
169
+ export const {
170
+ useGetContactSubjectsQuery,
171
+ useGetOrderQuery,
172
+ useGetOrdersQuery,
173
+ useSendContactMutation,
174
+ useUpdateEmailMutation,
175
+ useUpdatePasswordMutation,
176
+ useUpdateProfileMutation,
177
+ useGetProfileInfoQuery,
178
+ useCancelOrderMutation,
179
+ useGetCancellationReasonsQuery,
180
+ useGetBasketOffersQuery,
181
+ useGetFutureBasketOffersQuery,
182
+ useGetExpiredBasketOffersQuery,
183
+ useGetDiscountItemsQuery,
184
+ usePasswordResetMutation,
185
+ useAnonymizeMutation,
186
+ useGetLoyaltyBalanceQuery,
187
+ useGetLoyaltyTransactionsQuery
188
+ } = accountApi;