@akinon/next 1.47.0 → 1.48.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 +369 -0
- package/api/client.ts +50 -17
- 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/user.ts +3 -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 +9 -2
- 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 +91 -8
- 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 +31 -2
- package/utils/app-fetch.ts +6 -3
- 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/utils/server-variables.ts +2 -1
- package/with-pz-config.js +11 -1
package/components/input.tsx
CHANGED
|
@@ -1,17 +1,30 @@
|
|
|
1
1
|
import clsx from 'clsx';
|
|
2
|
-
import { forwardRef, FocusEvent, useState } from 'react';
|
|
2
|
+
import { forwardRef, FocusEvent, useState, Ref } from 'react';
|
|
3
3
|
import { Controller } from 'react-hook-form';
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
// @ts-ignore
|
|
6
|
+
import { PatternFormat, PatternFormatProps } from 'react-number-format';
|
|
5
7
|
import { InputProps } from '../types';
|
|
6
8
|
import { twMerge } from 'tailwind-merge';
|
|
7
9
|
|
|
10
|
+
const PatternFormatWithRef = forwardRef(
|
|
11
|
+
(props: PatternFormatProps, ref: Ref<HTMLInputElement>) => {
|
|
12
|
+
return <PatternFormat {...props} getInputRef={ref} />;
|
|
13
|
+
}
|
|
14
|
+
);
|
|
15
|
+
PatternFormatWithRef.displayName = 'PatternFormatWithRef';
|
|
16
|
+
|
|
8
17
|
export const Input = forwardRef<
|
|
9
18
|
HTMLInputElement,
|
|
10
19
|
InputProps &
|
|
11
20
|
Pick<
|
|
12
|
-
|
|
13
|
-
'
|
|
14
|
-
>
|
|
21
|
+
PatternFormatProps,
|
|
22
|
+
'mask' | 'allowEmptyFormatting' | 'onValueChange'
|
|
23
|
+
> & {
|
|
24
|
+
format?: string;
|
|
25
|
+
defaultValue?: string;
|
|
26
|
+
type?: string;
|
|
27
|
+
}
|
|
15
28
|
>((props, ref) => {
|
|
16
29
|
const [focused, setFocused] = useState(false);
|
|
17
30
|
const [hasValue, setHasValue] = useState(false);
|
|
@@ -37,6 +50,7 @@ export const Input = forwardRef<
|
|
|
37
50
|
),
|
|
38
51
|
props.className
|
|
39
52
|
);
|
|
53
|
+
|
|
40
54
|
const inputProps: any = {
|
|
41
55
|
id,
|
|
42
56
|
ref,
|
|
@@ -79,14 +93,14 @@ export const Input = forwardRef<
|
|
|
79
93
|
<Controller
|
|
80
94
|
name={props.name ?? ''}
|
|
81
95
|
control={props.control}
|
|
82
|
-
defaultValue={false}
|
|
83
96
|
render={({ field }) => (
|
|
84
|
-
<
|
|
97
|
+
<PatternFormatWithRef
|
|
85
98
|
format={format}
|
|
86
99
|
mask={mask ?? ''}
|
|
87
100
|
{...rest}
|
|
88
101
|
{...field}
|
|
89
102
|
{...inputProps}
|
|
103
|
+
type={props.type as 'text' | 'password' | 'tel'}
|
|
90
104
|
/>
|
|
91
105
|
)}
|
|
92
106
|
/>
|
package/components/link.tsx
CHANGED
|
@@ -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 (
|
|
19
|
-
return
|
|
20
|
+
if (!href) {
|
|
21
|
+
return '#';
|
|
20
22
|
}
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
if (typeof href === 'string' && !href.startsWith('http')) {
|
|
25
|
+
const pathnameWithoutLocale = href.replace(urlLocaleMatcherRegex, '');
|
|
26
|
+
const hrefWithLocale = `/${locale}${pathnameWithoutLocale}`;
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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 '
|
|
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';
|
package/components/price.tsx
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
|
-
|
|
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:
|
|
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
|
-
<
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
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
|
|
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
|
),
|
package/data/client/account.ts
CHANGED
|
@@ -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,
|
|
127
|
-
query: (body) =>
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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 }) => ({
|
package/data/client/address.ts
CHANGED
|
@@ -39,7 +39,10 @@ const addressApi = api.injectEndpoints({
|
|
|
39
39
|
query: (city) =>
|
|
40
40
|
buildClientRequestUrl(address.getRetailStoreTownships(city))
|
|
41
41
|
}),
|
|
42
|
-
addAddress: builder.mutation<
|
|
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) =>
|
|
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<
|
|
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
|
|
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<
|
|
70
|
-
|
|
71
|
-
|
|
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
|
|
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 }) => ({
|
package/data/client/api.ts
CHANGED
|
@@ -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 '
|
|
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
|
-
'
|
|
70
|
+
'MultiBasket',
|
|
71
71
|
'BasketB2b',
|
|
72
72
|
'DraftsB2b',
|
|
73
73
|
'Product',
|
package/data/client/b2b.ts
CHANGED
|
@@ -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;
|
package/data/client/basket.ts
CHANGED
|
@@ -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: ['
|
|
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: ['
|
|
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: ['
|
|
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: ['
|
|
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) => ({
|
package/data/client/checkout.ts
CHANGED
|
@@ -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;
|
package/data/client/user.ts
CHANGED
|
@@ -22,11 +22,12 @@ const userApi = api.injectEndpoints({
|
|
|
22
22
|
getCaptcha: build.query<GetCaptchaResponse, void>({
|
|
23
23
|
query: () => buildClientRequestUrl(user.captcha),
|
|
24
24
|
transformResponse: (response: { html: string }) => {
|
|
25
|
-
const
|
|
25
|
+
const siteKey = response.html.match(/data-sitekey="([^"]+)"/i)[1];
|
|
26
|
+
|
|
26
27
|
const csrfTokenMatch = response.html.match(
|
|
27
28
|
/name=['|"]csrfmiddlewaretoken['|"] value=['|"][^'"]+/gi
|
|
28
29
|
);
|
|
29
|
-
|
|
30
|
+
|
|
30
31
|
const csrfToken =
|
|
31
32
|
csrfTokenMatch?.[0].replace(
|
|
32
33
|
/name=['|"]csrfmiddlewaretoken['|"] value=['|"]/gi,
|
package/data/server/category.ts
CHANGED
|
@@ -8,7 +8,7 @@ import logger from '../../utils/log';
|
|
|
8
8
|
|
|
9
9
|
function getCategoryDataHandler(
|
|
10
10
|
pk: number,
|
|
11
|
-
searchParams?:
|
|
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?:
|
|
81
|
+
searchParams?: { [key: string]: string | string[] | undefined };
|
|
82
82
|
headers?: Record<string, string>;
|
|
83
83
|
}) => {
|
|
84
84
|
return Cache.wrap(
|
package/data/server/list.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { parse } from 'lossless-json';
|
|
|
7
7
|
import logger from '../../utils/log';
|
|
8
8
|
|
|
9
9
|
const getListDataHandler = (
|
|
10
|
-
searchParams:
|
|
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:
|
|
57
|
+
searchParams: { [key: string]: string | string[] | undefined };
|
|
58
58
|
headers?: Record<string, string>;
|
|
59
59
|
}) => {
|
|
60
60
|
return Cache.wrap(
|
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
|