@akinon/next 1.8.0 → 1.9.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 +13 -0
- package/api/client.ts +31 -14
- package/data/client/account.ts +11 -1
- package/data/client/b2b.ts +32 -0
- package/data/client/basket.ts +0 -3
- package/data/client/misc.ts +1 -1
- package/data/server/category.ts +13 -6
- package/data/server/index.ts +1 -0
- package/data/server/landingpage.ts +24 -0
- package/data/server/list.ts +11 -5
- package/data/server/seo.ts +1 -1
- package/data/server/special-page.ts +10 -5
- package/data/server/widget.ts +1 -1
- package/data/urls.ts +94 -76
- package/lib/cache.ts +35 -6
- package/package.json +4 -4
- package/redux/middlewares/checkout.ts +10 -4
- package/types/commerce/b2b.ts +61 -0
- package/types/commerce/index.ts +1 -0
- package/types/commerce/landingpage.ts +7 -0
- package/types/commerce/order.ts +11 -0
- package/types/index.ts +6 -0
- package/utils/app-fetch.ts +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @akinon/next
|
|
2
2
|
|
|
3
|
+
## 1.9.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- ZERO-2297: Add landing page structure
|
|
8
|
+
- ZERO-2317: Prevent browser cache using timestamp
|
|
9
|
+
- ZERO-2293: Upgrade Redux
|
|
10
|
+
- ZERO-2285: Allow custom headers in server data fetch functions
|
|
11
|
+
- ZERO-1577: Add option to exclude payment options from iframe
|
|
12
|
+
- ZERO-2282: Allow custom headers in client proxy API
|
|
13
|
+
- ZERO-2308: Add B2B endpoints and types
|
|
14
|
+
- ZERO-2202: Ensure commerce endpoints end with trailing slash
|
|
15
|
+
|
|
3
16
|
## 1.8.0
|
|
4
17
|
|
|
5
18
|
### Minor Changes
|
package/api/client.ts
CHANGED
|
@@ -29,7 +29,7 @@ async function proxyRequest(...args) {
|
|
|
29
29
|
responseType: 'json'
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
-
const slug = params.slug.join('/')
|
|
32
|
+
const slug = `${params.slug.join('/')}/`;
|
|
33
33
|
const options_ = JSON.parse(
|
|
34
34
|
decodeURIComponent((searchParams.get('options') as string) ?? '{}')
|
|
35
35
|
);
|
|
@@ -37,23 +37,34 @@ async function proxyRequest(...args) {
|
|
|
37
37
|
|
|
38
38
|
Object.assign(options, options_);
|
|
39
39
|
|
|
40
|
-
Array.from(searchParams.keys())
|
|
41
|
-
|
|
40
|
+
Array.from(searchParams.keys())
|
|
41
|
+
.filter((key) => !['slug', 'options'].includes(key))
|
|
42
|
+
.forEach((key) => {
|
|
42
43
|
urlSearchParams.append(key, `${searchParams.get(key)}`);
|
|
43
|
-
}
|
|
44
|
-
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const extraHeaders = Object.fromEntries(req.headers.entries());
|
|
47
|
+
|
|
48
|
+
[
|
|
49
|
+
'x-forwarded-host',
|
|
50
|
+
'x-forwarded-for',
|
|
51
|
+
'x-forwarded-proto',
|
|
52
|
+
'x-forwarded-port',
|
|
53
|
+
'origin',
|
|
54
|
+
'host',
|
|
55
|
+
'referer',
|
|
56
|
+
'accept',
|
|
57
|
+
'content-length',
|
|
58
|
+
'content-type'
|
|
59
|
+
].forEach((header) => delete extraHeaders[header]);
|
|
45
60
|
|
|
46
61
|
const fetchOptions = {
|
|
47
62
|
method: req.method,
|
|
48
63
|
headers: {
|
|
64
|
+
...extraHeaders,
|
|
49
65
|
'X-Requested-With': 'XMLHttpRequest',
|
|
50
|
-
'X-CSRFToken': `${req.headers.get('x-csrftoken')}`,
|
|
51
|
-
'x-currency': `${req.headers.get('x-currency')}`,
|
|
52
|
-
'Accept-Language': `${req.headers.get('Accept-Language')}`,
|
|
53
|
-
'User-Agent': `${req.headers.get('User-Agent')}`,
|
|
54
66
|
Referer: commerceUrl,
|
|
55
|
-
Accept: options.accept
|
|
56
|
-
Cookie: `${req.headers.get('cookie')}`
|
|
67
|
+
Accept: options.accept
|
|
57
68
|
}
|
|
58
69
|
} as RequestInit;
|
|
59
70
|
|
|
@@ -68,7 +79,13 @@ async function proxyRequest(...args) {
|
|
|
68
79
|
try {
|
|
69
80
|
body = await req.json();
|
|
70
81
|
} catch (error) {
|
|
71
|
-
|
|
82
|
+
logger.error(
|
|
83
|
+
`Client Proxy Request - Error while parsing request body to JSON`,
|
|
84
|
+
{
|
|
85
|
+
url: req.url,
|
|
86
|
+
error
|
|
87
|
+
}
|
|
88
|
+
);
|
|
72
89
|
}
|
|
73
90
|
|
|
74
91
|
Object.keys(body ?? {}).forEach((key) => {
|
|
@@ -82,8 +99,8 @@ async function proxyRequest(...args) {
|
|
|
82
99
|
|
|
83
100
|
let url = `${commerceUrl}/${slug.replace(/,/g, '/')}`;
|
|
84
101
|
|
|
85
|
-
if (options.useTrailingSlash) {
|
|
86
|
-
url
|
|
102
|
+
if (!options.useTrailingSlash) {
|
|
103
|
+
url = url.replace(/\/$/, '');
|
|
87
104
|
}
|
|
88
105
|
|
|
89
106
|
if (urlSearchParams.toString().length) {
|
package/data/client/account.ts
CHANGED
|
@@ -7,7 +7,8 @@ import {
|
|
|
7
7
|
AccountOrderCancellation,
|
|
8
8
|
AccountOrderCancellationReason,
|
|
9
9
|
ContactFormType,
|
|
10
|
-
Order
|
|
10
|
+
Order,
|
|
11
|
+
Quotations
|
|
11
12
|
} from '../../types';
|
|
12
13
|
|
|
13
14
|
interface GetOrdersResponse {
|
|
@@ -21,6 +22,11 @@ interface GetOrdersParams {
|
|
|
21
22
|
createdDate?: string;
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
export interface GetQuotationsResponse {
|
|
26
|
+
count: number;
|
|
27
|
+
results: Quotations[];
|
|
28
|
+
}
|
|
29
|
+
|
|
24
30
|
export type PasswordResetType = {
|
|
25
31
|
password: string;
|
|
26
32
|
repeatPassword: string;
|
|
@@ -102,6 +108,9 @@ const accountApi = api.injectEndpoints({
|
|
|
102
108
|
query: ({ page, limit, createdDate } = {}) =>
|
|
103
109
|
buildClientRequestUrl(account.getOrders({ page, limit, createdDate }))
|
|
104
110
|
}),
|
|
111
|
+
getQuotations: builder.query<GetQuotationsResponse, void>({
|
|
112
|
+
query: () => buildClientRequestUrl(account.getQuotations)
|
|
113
|
+
}),
|
|
105
114
|
sendContact: builder.mutation<void, ContactFormType>({
|
|
106
115
|
query: (body) => ({
|
|
107
116
|
url: buildClientRequestUrl(account.sendContact, {
|
|
@@ -170,6 +179,7 @@ export const {
|
|
|
170
179
|
useGetContactSubjectsQuery,
|
|
171
180
|
useGetOrderQuery,
|
|
172
181
|
useGetOrdersQuery,
|
|
182
|
+
useGetQuotationsQuery,
|
|
173
183
|
useSendContactMutation,
|
|
174
184
|
useUpdateEmailMutation,
|
|
175
185
|
useUpdatePasswordMutation,
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { buildClientRequestUrl } from '../../utils';
|
|
2
|
+
import { api } from './api';
|
|
3
|
+
import { b2b } from '../urls';
|
|
4
|
+
import { Basket, BasketParams, BasketResponse, Division, GetResponse } from '../../types';
|
|
5
|
+
|
|
6
|
+
const b2bApi = api.injectEndpoints({
|
|
7
|
+
endpoints: (build) => ({
|
|
8
|
+
getBasket: build.query<Basket, void>({
|
|
9
|
+
query: () =>
|
|
10
|
+
buildClientRequestUrl(b2b.basket, {
|
|
11
|
+
contentType: 'application/json'
|
|
12
|
+
}),
|
|
13
|
+
transformResponse: (response: { basket: Basket }) => response.basket,
|
|
14
|
+
providesTags: ['Basket']
|
|
15
|
+
}),
|
|
16
|
+
getDivisions: build.query<GetResponse<Division>, void>({
|
|
17
|
+
query: () => buildClientRequestUrl(b2b.divisions)
|
|
18
|
+
}),
|
|
19
|
+
addToBasket: build.mutation<BasketResponse, BasketParams>({
|
|
20
|
+
query: (body) => ({
|
|
21
|
+
url: buildClientRequestUrl(b2b.basket, {
|
|
22
|
+
contentType: 'application/json'
|
|
23
|
+
}),
|
|
24
|
+
method: 'POST',
|
|
25
|
+
body
|
|
26
|
+
})
|
|
27
|
+
}),
|
|
28
|
+
}),
|
|
29
|
+
overrideExisting: true
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export const { useGetBasketQuery, useLazyGetDivisionsQuery, useAddToBasketMutation } = b2bApi;
|
package/data/client/basket.ts
CHANGED
|
@@ -39,7 +39,6 @@ export const basketApi = api.injectEndpoints({
|
|
|
39
39
|
clearBasket: build.mutation<Basket, void>({
|
|
40
40
|
query: (body) => ({
|
|
41
41
|
url: buildClientRequestUrl(basket.getBasket, {
|
|
42
|
-
useTrailingSlash: false,
|
|
43
42
|
contentType: 'application/json'
|
|
44
43
|
}),
|
|
45
44
|
method: 'DELETE',
|
|
@@ -51,7 +50,6 @@ export const basketApi = api.injectEndpoints({
|
|
|
51
50
|
applyVoucherCode: build.mutation<Basket, { voucher_code: string }>({
|
|
52
51
|
query: (body) => ({
|
|
53
52
|
url: buildClientRequestUrl(basket.getBasket, {
|
|
54
|
-
useTrailingSlash: false,
|
|
55
53
|
contentType: 'application/json'
|
|
56
54
|
}),
|
|
57
55
|
method: 'PATCH',
|
|
@@ -62,7 +60,6 @@ export const basketApi = api.injectEndpoints({
|
|
|
62
60
|
removeVoucherCode: build.mutation<Basket, void>({
|
|
63
61
|
query: () => ({
|
|
64
62
|
url: buildClientRequestUrl(basket.getBasket, {
|
|
65
|
-
useTrailingSlash: false,
|
|
66
63
|
contentType: 'application/json'
|
|
67
64
|
}),
|
|
68
65
|
method: 'PATCH',
|
package/data/client/misc.ts
CHANGED
|
@@ -77,7 +77,7 @@ export const miscApi = api.injectEndpoints({
|
|
|
77
77
|
}),
|
|
78
78
|
getWidget: builder.query<WidgetResultType<any>, string>({
|
|
79
79
|
query: (slug: string) => ({
|
|
80
|
-
url: buildClientRequestUrl(widgets.
|
|
80
|
+
url: buildClientRequestUrl(widgets.getWidget(slug))
|
|
81
81
|
})
|
|
82
82
|
}),
|
|
83
83
|
getMenu: builder.query<MenuItemType[], number>({
|
package/data/server/category.ts
CHANGED
|
@@ -6,7 +6,11 @@ import { Cache, CacheKey } from '../../lib/cache';
|
|
|
6
6
|
import { parse } from 'lossless-json';
|
|
7
7
|
import logger from '../../utils/log';
|
|
8
8
|
|
|
9
|
-
function getCategoryDataHandler(
|
|
9
|
+
function getCategoryDataHandler(
|
|
10
|
+
pk: number,
|
|
11
|
+
searchParams?: URLSearchParams,
|
|
12
|
+
headers?: Record<string, string>
|
|
13
|
+
) {
|
|
10
14
|
return async function () {
|
|
11
15
|
const params = generateCommerceSearchParams(searchParams);
|
|
12
16
|
|
|
@@ -15,7 +19,8 @@ function getCategoryDataHandler(pk: number, searchParams?: URLSearchParams) {
|
|
|
15
19
|
{
|
|
16
20
|
headers: {
|
|
17
21
|
Accept: 'application/json',
|
|
18
|
-
'Content-Type': 'application/json'
|
|
22
|
+
'Content-Type': 'application/json',
|
|
23
|
+
...(headers ?? {})
|
|
19
24
|
}
|
|
20
25
|
},
|
|
21
26
|
FetchResponseType.TEXT
|
|
@@ -59,14 +64,16 @@ function getCategoryDataHandler(pk: number, searchParams?: URLSearchParams) {
|
|
|
59
64
|
|
|
60
65
|
export const getCategoryData = ({
|
|
61
66
|
pk,
|
|
62
|
-
searchParams
|
|
67
|
+
searchParams,
|
|
68
|
+
headers
|
|
63
69
|
}: {
|
|
64
70
|
pk: number;
|
|
65
71
|
searchParams?: URLSearchParams;
|
|
72
|
+
headers?: Record<string, string>;
|
|
66
73
|
}) => {
|
|
67
74
|
return Cache.wrap(
|
|
68
|
-
CacheKey.Category(pk, searchParams),
|
|
69
|
-
getCategoryDataHandler(pk, searchParams),
|
|
75
|
+
CacheKey.Category(pk, searchParams, headers),
|
|
76
|
+
getCategoryDataHandler(pk, searchParams, headers),
|
|
70
77
|
{
|
|
71
78
|
expire: 300
|
|
72
79
|
}
|
|
@@ -75,7 +82,7 @@ export const getCategoryData = ({
|
|
|
75
82
|
|
|
76
83
|
function getCategoryBySlugDataHandler(slug: string) {
|
|
77
84
|
return async function () {
|
|
78
|
-
|
|
85
|
+
const rawData = await appFetch<string>(
|
|
79
86
|
category.getCategoryBySlug(slug),
|
|
80
87
|
{
|
|
81
88
|
headers: {
|
package/data/server/index.ts
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { landingpage } from '../urls';
|
|
2
|
+
import { LandingPage } from '../../types/commerce/landingpage';
|
|
3
|
+
import appFetch from '../../utils/app-fetch';
|
|
4
|
+
import { Cache, CacheKey } from '../../lib/cache';
|
|
5
|
+
|
|
6
|
+
const getLandingPageHandler = (pk: number) => {
|
|
7
|
+
return async function () {
|
|
8
|
+
const data = await appFetch<LandingPage>(
|
|
9
|
+
landingpage.getLandingPageByPk(pk),
|
|
10
|
+
{
|
|
11
|
+
headers: {
|
|
12
|
+
Accept: 'application/json',
|
|
13
|
+
'Content-Type': 'application/json'
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
return data;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const getLandingPageData = ({ pk }: { pk: number }) => {
|
|
23
|
+
return Cache.wrap(CacheKey.LandingPage(pk), getLandingPageHandler(pk));
|
|
24
|
+
};
|
package/data/server/list.ts
CHANGED
|
@@ -6,7 +6,10 @@ import appFetch, { FetchResponseType } from '../../utils/app-fetch';
|
|
|
6
6
|
import { parse } from 'lossless-json';
|
|
7
7
|
import logger from '../../utils/log';
|
|
8
8
|
|
|
9
|
-
const getListDataHandler = (
|
|
9
|
+
const getListDataHandler = (
|
|
10
|
+
searchParams: URLSearchParams,
|
|
11
|
+
headers?: Record<string, string>
|
|
12
|
+
) => {
|
|
10
13
|
return async function () {
|
|
11
14
|
const params = generateCommerceSearchParams(searchParams);
|
|
12
15
|
|
|
@@ -15,7 +18,8 @@ const getListDataHandler = (searchParams: URLSearchParams) => {
|
|
|
15
18
|
{
|
|
16
19
|
headers: {
|
|
17
20
|
Accept: 'application/json',
|
|
18
|
-
'Content-Type': 'application/json'
|
|
21
|
+
'Content-Type': 'application/json',
|
|
22
|
+
...(headers ?? {})
|
|
19
23
|
}
|
|
20
24
|
},
|
|
21
25
|
FetchResponseType.TEXT
|
|
@@ -42,13 +46,15 @@ const getListDataHandler = (searchParams: URLSearchParams) => {
|
|
|
42
46
|
};
|
|
43
47
|
|
|
44
48
|
export const getListData = async ({
|
|
45
|
-
searchParams
|
|
49
|
+
searchParams,
|
|
50
|
+
headers
|
|
46
51
|
}: {
|
|
47
52
|
searchParams: URLSearchParams;
|
|
53
|
+
headers?: Record<string, string>;
|
|
48
54
|
}) => {
|
|
49
55
|
return Cache.wrap(
|
|
50
|
-
CacheKey.List(searchParams),
|
|
51
|
-
getListDataHandler(searchParams),
|
|
56
|
+
CacheKey.List(searchParams, headers),
|
|
57
|
+
getListDataHandler(searchParams, headers),
|
|
52
58
|
{
|
|
53
59
|
expire: 300
|
|
54
60
|
}
|
package/data/server/seo.ts
CHANGED
|
@@ -29,7 +29,7 @@ function getSeoDataHandler(url: string) {
|
|
|
29
29
|
try {
|
|
30
30
|
data = await appFetch(misc.cmsSeo(url));
|
|
31
31
|
} catch (error) {
|
|
32
|
-
logger.error('Error while fetching seo data', { url, error });
|
|
32
|
+
// logger.error('Error while fetching seo data', { url, error });
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
return data;
|
|
@@ -3,10 +3,12 @@ import { category } from '../urls';
|
|
|
3
3
|
import { GetCategoryResponse } from '../../types';
|
|
4
4
|
import { generateCommerceSearchParams } from '../../utils';
|
|
5
5
|
import appFetch from '../../utils/app-fetch';
|
|
6
|
+
import header from '../../redux/reducers/header';
|
|
6
7
|
|
|
7
8
|
const getSpecialPageDataHandler = (
|
|
8
9
|
pk: number,
|
|
9
|
-
searchParams: URLSearchParams
|
|
10
|
+
searchParams: URLSearchParams,
|
|
11
|
+
headers?: Record<string, string>
|
|
10
12
|
) => {
|
|
11
13
|
return async function () {
|
|
12
14
|
const params = generateCommerceSearchParams(searchParams);
|
|
@@ -16,7 +18,8 @@ const getSpecialPageDataHandler = (
|
|
|
16
18
|
{
|
|
17
19
|
headers: {
|
|
18
20
|
Accept: 'application/json',
|
|
19
|
-
'Content-Type': 'application/json'
|
|
21
|
+
'Content-Type': 'application/json',
|
|
22
|
+
...(headers ?? {})
|
|
20
23
|
}
|
|
21
24
|
}
|
|
22
25
|
);
|
|
@@ -27,14 +30,16 @@ const getSpecialPageDataHandler = (
|
|
|
27
30
|
|
|
28
31
|
export const getSpecialPageData = async ({
|
|
29
32
|
pk,
|
|
30
|
-
searchParams
|
|
33
|
+
searchParams,
|
|
34
|
+
headers
|
|
31
35
|
}: {
|
|
32
36
|
pk: number;
|
|
33
37
|
searchParams: URLSearchParams;
|
|
38
|
+
headers?: Record<string, string>;
|
|
34
39
|
}) => {
|
|
35
40
|
return Cache.wrap(
|
|
36
|
-
CacheKey.SpecialPage(pk, searchParams),
|
|
37
|
-
getSpecialPageDataHandler(pk, searchParams),
|
|
41
|
+
CacheKey.SpecialPage(pk, searchParams, headers),
|
|
42
|
+
getSpecialPageDataHandler(pk, searchParams, headers),
|
|
38
43
|
{
|
|
39
44
|
expire: 300
|
|
40
45
|
}
|
package/data/server/widget.ts
CHANGED
package/data/urls.ts
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
+
/* eslint-disable @akinon/projectzero/client-url */
|
|
1
2
|
import Settings from 'settings';
|
|
2
3
|
|
|
3
4
|
const API_URL = Settings.commerceUrl;
|
|
4
5
|
|
|
5
6
|
export const account = {
|
|
6
|
-
order: '/users/orders',
|
|
7
|
-
orderId: (id: string | string[]) => `/users/orders/${id}
|
|
8
|
-
cancelOrder: (id: string) => `/orders/${id}/cancel
|
|
9
|
-
cancellationReasons: '/users/orders/cancellation_reasons',
|
|
10
|
-
updatePassword: '/users/password/change',
|
|
11
|
-
updateEmail: '/users/email-change',
|
|
12
|
-
updateProfile: '/users/profile',
|
|
13
|
-
getContactSubjects: '/users/contact-us-subjects',
|
|
7
|
+
order: '/users/orders/',
|
|
8
|
+
orderId: (id: string | string[]) => `/users/orders/${id}/`,
|
|
9
|
+
cancelOrder: (id: string) => `/orders/${id}/cancel/`,
|
|
10
|
+
cancellationReasons: '/users/orders/cancellation_reasons/',
|
|
11
|
+
updatePassword: '/users/password/change/',
|
|
12
|
+
updateEmail: '/users/email-change/',
|
|
13
|
+
updateProfile: '/users/profile/',
|
|
14
|
+
getContactSubjects: '/users/contact-us-subjects/',
|
|
14
15
|
getOrders: ({
|
|
15
16
|
page,
|
|
16
17
|
limit,
|
|
@@ -20,9 +21,10 @@ export const account = {
|
|
|
20
21
|
limit?: number;
|
|
21
22
|
createdDate?: string;
|
|
22
23
|
}) =>
|
|
23
|
-
`/users/orders
|
|
24
|
+
`/users/orders/?page=${page || 1}&limit=${limit || 12} ${
|
|
24
25
|
createdDate ? `&created_date__gte=${createdDate}` : ''
|
|
25
26
|
}`,
|
|
27
|
+
getQuotations: '/b2b/my-quotations',
|
|
26
28
|
sendContact: '/users/contact-us',
|
|
27
29
|
passwordReset: (slug: string) => `/users/reset/${slug}`,
|
|
28
30
|
anonymize: '/users/anonymize/',
|
|
@@ -31,95 +33,100 @@ export const account = {
|
|
|
31
33
|
};
|
|
32
34
|
|
|
33
35
|
export const address = {
|
|
34
|
-
base: '/address',
|
|
35
|
-
countries: '/address/country',
|
|
36
|
-
getAddresses: '/address/detailed',
|
|
37
|
-
getCities: (country: string) => `/address/city
|
|
38
|
-
getTownships: (city: string) => `/address/township
|
|
39
|
-
getDistricts: (township: string) => `/address/district
|
|
40
|
-
getRetailStore: '/address/city
|
|
36
|
+
base: '/address/',
|
|
37
|
+
countries: '/address/country/',
|
|
38
|
+
getAddresses: '/address/detailed/',
|
|
39
|
+
getCities: (country: string) => `/address/city/?country=${country}`,
|
|
40
|
+
getTownships: (city: string) => `/address/township/?city=${city}`,
|
|
41
|
+
getDistricts: (township: string) => `/address/district/?township=${township}`,
|
|
42
|
+
getRetailStore: '/address/city/?retailstore__isnull=false',
|
|
41
43
|
getRetailStoreCities: (country: string) =>
|
|
42
|
-
`/address/city
|
|
44
|
+
`/address/city/?retailstore__isnull=false&country=${country}`,
|
|
43
45
|
getRetailStoreTownships: (city: string) =>
|
|
44
|
-
`/address/township
|
|
45
|
-
editAddress: (pk: number) => `/address/${pk}
|
|
46
|
-
removeAddress: (id: number) => `/address/${id}
|
|
47
|
-
setDefaultAddress: (pk: number) => `/address/${pk}
|
|
46
|
+
`/address/township/?retailstore__isnull=false&city=${city}`,
|
|
47
|
+
editAddress: (pk: number) => `/address/${pk}/`,
|
|
48
|
+
removeAddress: (id: number) => `/address/${id}/`,
|
|
49
|
+
setDefaultAddress: (pk: number) => `/address/${pk}/`
|
|
48
50
|
};
|
|
49
51
|
|
|
50
52
|
export const basket = {
|
|
51
|
-
getBasket: '/baskets/basket'
|
|
53
|
+
getBasket: '/baskets/basket/'
|
|
52
54
|
};
|
|
53
55
|
|
|
54
56
|
export const category = {
|
|
55
|
-
// eslint-disable-next-line projectzero/client-url
|
|
56
57
|
list: '/list/',
|
|
57
58
|
getCategoryByPk: (pk: number) => `/category/${pk}/`,
|
|
58
|
-
getCategoryBySlug: (slug: string) => `/${slug}
|
|
59
|
+
getCategoryBySlug: (slug: string) => `/${slug}/`,
|
|
59
60
|
getSpecialPageByPk: (pk: number) => `/special-page/${pk}/`
|
|
60
61
|
};
|
|
61
62
|
|
|
62
63
|
export const checkout = {
|
|
63
|
-
get3dRedirectForm: '/orders/redirect-three-d',
|
|
64
|
-
fetchCheckout: '/orders/checkout',
|
|
64
|
+
get3dRedirectForm: '/orders/redirect-three-d/',
|
|
65
|
+
fetchCheckout: '/orders/checkout/',
|
|
65
66
|
fetchCheckoutResult: (token: string) =>
|
|
66
|
-
`/orders/completed/${token}
|
|
67
|
-
getContract: (slug: string) => `/orders/contract
|
|
68
|
-
completeCreditCardPayment:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
67
|
+
`/orders/completed/${token}/?format=json`,
|
|
68
|
+
getContract: (slug: string) => `/orders/contract/?contract_name=${slug}`,
|
|
69
|
+
completeCreditCardPayment:
|
|
70
|
+
'/orders/checkout/?page=CreditCardConfirmationPage',
|
|
71
|
+
completeFundsTransfer: '/orders/checkout/?page=FundsTransferPage',
|
|
72
|
+
guestLogin: '/orders/checkout/?page=IndexPage',
|
|
73
|
+
setDeliveryOption: '/orders/checkout/?page=DeliveryOptionSelectionPage',
|
|
74
|
+
setAddresses: '/orders/checkout/?page=AddressSelectionPage',
|
|
75
|
+
setShippingOption: '/orders/checkout/?page=ShippingOptionSelectionPage',
|
|
76
|
+
setPaymentOption: '/orders/checkout/?page=PaymentOptionSelectionPage',
|
|
77
|
+
setBinNumber: '/orders/checkout/?page=BinNumberPage',
|
|
78
|
+
setMasterpassBinNumber: '/orders/checkout/?page=MasterpassBinNumberPage',
|
|
79
|
+
setInstallmentOption: '/orders/checkout/?page=InstallmentSelectionPage',
|
|
78
80
|
setMasterPassInstallmentOption:
|
|
79
|
-
'/orders/checkout
|
|
80
|
-
setFundsTransferOption: '/orders/checkout
|
|
81
|
-
getCheckoutProviders: '/orders/checkout
|
|
82
|
-
|
|
81
|
+
'/orders/checkout/?page=MasterpassInstallmentPage',
|
|
82
|
+
setFundsTransferOption: '/orders/checkout/?page=FundsTransferChoicePage',
|
|
83
|
+
getCheckoutProviders: '/orders/checkout-provider-list/',
|
|
84
|
+
getCheckoutProvidersIndex: '/orders/checkout/?page=CheckoutProviderIndexPage',
|
|
85
|
+
setCheckoutProvider: '/orders/checkout/?page=CheckoutProviderSelectionPage',
|
|
83
86
|
completeRedirectionPayment:
|
|
84
|
-
'/orders/checkout
|
|
87
|
+
'/orders/checkout/?page=RedirectionPaymentSelectedPage',
|
|
85
88
|
confirmationPaymentSelect:
|
|
86
|
-
'/orders/checkout
|
|
87
|
-
confirmationQuery: '/orders/checkout
|
|
88
|
-
confirmationComplete:
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
'/orders/checkout/?page=ConfirmationPaymentSelectedPage',
|
|
90
|
+
confirmationQuery: '/orders/checkout/?page=ConfirmationPaymentQueryPage',
|
|
91
|
+
confirmationComplete:
|
|
92
|
+
'/orders/checkout/?page=ConfirmationPaymentCompletePage',
|
|
93
|
+
completeLoyaltyPayment: '/orders/checkout/?page=LoyaltyPaymentSelectedPage',
|
|
94
|
+
loyaltyMoneyUsage: '/orders/checkout/?page=LoyaltyMoneyUsagePage',
|
|
95
|
+
setOrderNote: '/orders/checkout/?page=OrderNotePage'
|
|
92
96
|
};
|
|
93
97
|
|
|
94
98
|
export const flatpage = {
|
|
95
99
|
getFlatPageByPk: (pk: number) => `/shop-flat-page/${pk}/`
|
|
96
100
|
};
|
|
97
101
|
|
|
102
|
+
export const landingpage = {
|
|
103
|
+
getLandingPageByPk: (pk: number) => `/landing-page/${pk}/`
|
|
104
|
+
};
|
|
105
|
+
|
|
98
106
|
export const misc = {
|
|
99
|
-
autocomplete: '/autocomplete',
|
|
100
|
-
stores: '/stores',
|
|
101
|
-
menu: '/misc/menus/generate',
|
|
107
|
+
autocomplete: '/autocomplete/',
|
|
108
|
+
stores: '/stores/',
|
|
109
|
+
menu: '/misc/menus/generate/',
|
|
102
110
|
prettyUrl: (pathname: string) => `/misc/pretty-url${pathname}/`,
|
|
103
111
|
prettyUrls: (pathname: string) => `/pretty_urls/?new_path__exact=${pathname}`,
|
|
104
|
-
emailSubscription: '/email-subscription',
|
|
112
|
+
emailSubscription: '/email-subscription/',
|
|
105
113
|
menus: (depthHeight: number, parent?: string) =>
|
|
106
114
|
`/menus/generate/?depth_height=${depthHeight}${
|
|
107
115
|
parent ? `&parent=${parent}` : ''
|
|
108
116
|
}`,
|
|
109
|
-
// eslint-disable-next-line projectzero/client-url
|
|
110
117
|
cmsSeo: (slug: string | string[]) => `/cms/seo/?url=${slug ? slug : '/'}`,
|
|
111
|
-
setCurrency: '/users/activate-currency'
|
|
118
|
+
setCurrency: '/users/activate-currency/'
|
|
112
119
|
};
|
|
113
120
|
|
|
114
121
|
export const product = {
|
|
115
122
|
category: (productId: number) => `/product/${productId}/category/`,
|
|
116
|
-
installments: (productId: number) => `/payments/cards/product/${productId}
|
|
117
|
-
getProductByPk: (pk: number) => `/product/${pk}
|
|
118
|
-
getGroupProductByPk: (pk: number) => `/group-product/${pk}
|
|
123
|
+
installments: (productId: number) => `/payments/cards/product/${productId}/`,
|
|
124
|
+
getProductByPk: (pk: number) => `/product/${pk}/`,
|
|
125
|
+
getGroupProductByPk: (pk: number) => `/group-product/${pk}/`,
|
|
119
126
|
getRetailStoreStock: (productPk: string, queryString: string) =>
|
|
120
|
-
`/retail_store_stock/${productPk}
|
|
121
|
-
addProduct: '/baskets/basket',
|
|
122
|
-
slug: (slug: string) => `/${slug}
|
|
127
|
+
`/retail_store_stock/${productPk}/?${queryString}`,
|
|
128
|
+
addProduct: '/baskets/basket/',
|
|
129
|
+
slug: (slug: string) => `/${slug}/`,
|
|
123
130
|
categoryUrl: (pk: number) => `/products/${pk}/category_nodes/?limit=1`,
|
|
124
131
|
breadcrumbUrl: (menuitemmodel: string) =>
|
|
125
132
|
`/menus/generate_breadcrumb/?item=${menuitemmodel}&generator_name=menu_item`
|
|
@@ -127,28 +134,38 @@ export const product = {
|
|
|
127
134
|
|
|
128
135
|
export const wishlist = {
|
|
129
136
|
getFavorites: ({ page, limit }: { page?: number; limit?: number }) =>
|
|
130
|
-
`/wishlists/favourite-products
|
|
131
|
-
addFavorite: '/wishlists/favourite-products',
|
|
132
|
-
removeFavorite: (favPk: number) => `/wishlists/favourite-products/${favPk}
|
|
133
|
-
addStockAlert: '/wishlists/product-alerts'
|
|
137
|
+
`/wishlists/favourite-products/?limit=${limit || 12}&page=${page || 1}`,
|
|
138
|
+
addFavorite: '/wishlists/favourite-products/',
|
|
139
|
+
removeFavorite: (favPk: number) => `/wishlists/favourite-products/${favPk}/`,
|
|
140
|
+
addStockAlert: '/wishlists/product-alerts/'
|
|
134
141
|
};
|
|
135
142
|
|
|
136
143
|
export const user = {
|
|
137
|
-
currentUser: '/current_user',
|
|
138
|
-
login: '/users/login',
|
|
139
|
-
// eslint-disable-next-line projectzero/client-url
|
|
144
|
+
currentUser: '/current_user/',
|
|
145
|
+
login: '/users/login/',
|
|
140
146
|
register: '/users/registration/',
|
|
141
|
-
logout: '/users/logout',
|
|
142
|
-
captcha: '/users/pz-captcha',
|
|
143
|
-
profiles: '/users/profile',
|
|
144
|
-
forgotPassword: '/users/password/reset',
|
|
145
|
-
changeEmailVerification: (token: string) =>
|
|
146
|
-
|
|
147
|
-
|
|
147
|
+
logout: '/users/logout/',
|
|
148
|
+
captcha: '/users/pz-captcha/',
|
|
149
|
+
profiles: '/users/profile/',
|
|
150
|
+
forgotPassword: '/users/password/reset/',
|
|
151
|
+
changeEmailVerification: (token: string) =>
|
|
152
|
+
`/users/email-set-primary/${token}/`,
|
|
153
|
+
confirmEmailVerification: (token: string) =>
|
|
154
|
+
`/users/registration/account-confirm-email/${token}/`,
|
|
155
|
+
csrfToken: '/csrf_token/'
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
export const b2b = {
|
|
159
|
+
basket: '/b2b/basket/',
|
|
160
|
+
saveBasket: '/b2b/basket/save/',
|
|
161
|
+
draftBaskets: '/b2b/basket/drafts/',
|
|
162
|
+
divisions: '/b2b/my-divisions/',
|
|
163
|
+
myQuotations: '/b2b/my-quotations/',
|
|
164
|
+
loadBasket: (id) => `/b2b/basket/${id}/load/`
|
|
148
165
|
};
|
|
149
166
|
|
|
150
167
|
export const widgets = {
|
|
151
|
-
|
|
168
|
+
getWidget: (slug: string) => `/widgets/${slug}/`
|
|
152
169
|
};
|
|
153
170
|
|
|
154
171
|
const URLS = {
|
|
@@ -166,6 +183,7 @@ const URLS = {
|
|
|
166
183
|
};
|
|
167
184
|
|
|
168
185
|
const UrlProxyHandler = {
|
|
186
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
169
187
|
get(target, prop, receiver) {
|
|
170
188
|
if (typeof target[prop] === 'function') {
|
|
171
189
|
return new Proxy(target[prop], {
|
package/lib/cache.ts
CHANGED
|
@@ -4,19 +4,48 @@ import { CacheOptions } from '../types';
|
|
|
4
4
|
import logger from '../utils/log';
|
|
5
5
|
import { ServerVariables } from '../utils/server-variables';
|
|
6
6
|
|
|
7
|
+
const hashCacheKey = (object?: Record<string, string>) => {
|
|
8
|
+
if (!object) {
|
|
9
|
+
return '';
|
|
10
|
+
}
|
|
11
|
+
const sortedFilters = Object.entries(object).sort(([firstKey], [secondKey]) =>
|
|
12
|
+
firstKey.localeCompare(secondKey)
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
const cacheKey = Buffer.from(
|
|
16
|
+
JSON.stringify(Object.fromEntries(sortedFilters))
|
|
17
|
+
).toString('base64');
|
|
18
|
+
|
|
19
|
+
return `_${encodeURIComponent(cacheKey)}`;
|
|
20
|
+
};
|
|
7
21
|
export const CacheKey = {
|
|
8
|
-
List: (searchParams: URLSearchParams) =>
|
|
9
|
-
`list_${encodeURIComponent(JSON.stringify(searchParams))}
|
|
10
|
-
|
|
11
|
-
|
|
22
|
+
List: (searchParams: URLSearchParams, headers?: Record<string, string>) =>
|
|
23
|
+
`list_${encodeURIComponent(JSON.stringify(searchParams))}${hashCacheKey(
|
|
24
|
+
headers
|
|
25
|
+
)}`,
|
|
26
|
+
Category: (
|
|
27
|
+
pk: number,
|
|
28
|
+
searchParams?: URLSearchParams,
|
|
29
|
+
headers?: Record<string, string>
|
|
30
|
+
) =>
|
|
31
|
+
`category_${pk}_${encodeURIComponent(
|
|
32
|
+
JSON.stringify(searchParams)
|
|
33
|
+
)}${hashCacheKey(headers)}`,
|
|
12
34
|
CategorySlug: (slug: string) => `category_${slug}`,
|
|
13
|
-
SpecialPage: (
|
|
14
|
-
|
|
35
|
+
SpecialPage: (
|
|
36
|
+
pk: number,
|
|
37
|
+
searchParams: URLSearchParams,
|
|
38
|
+
headers?: Record<string, string>
|
|
39
|
+
) =>
|
|
40
|
+
`special_page_${pk}_${encodeURIComponent(
|
|
41
|
+
JSON.stringify(searchParams)
|
|
42
|
+
)}${hashCacheKey(headers)}`,
|
|
15
43
|
Product: (pk: number, searchParams: URLSearchParams) =>
|
|
16
44
|
`product_${pk}_${encodeURIComponent(JSON.stringify(searchParams))}`,
|
|
17
45
|
GroupProduct: (pk: number, searchParams: URLSearchParams) =>
|
|
18
46
|
`group_product_${pk}_${encodeURIComponent(JSON.stringify(searchParams))}`,
|
|
19
47
|
FlatPage: (pk: number) => `flat_page_${pk}`,
|
|
48
|
+
LandingPage: (pk: number) => `landing_page_${pk}`,
|
|
20
49
|
Widget: (slug: string) => `widget_${slug}`,
|
|
21
50
|
PrettyUrl: (pathname: string) => `pretty_url_${pathname}`,
|
|
22
51
|
Menu: (depth: number, parent?: string) =>
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akinon/next",
|
|
3
3
|
"description": "Core package for Project Zero Next",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.9.0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
@@ -14,13 +14,13 @@
|
|
|
14
14
|
"pz-postdev": "bin/pz-postdev.js"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@reduxjs/toolkit": "1.9.
|
|
17
|
+
"@reduxjs/toolkit": "1.9.7",
|
|
18
18
|
"cross-spawn": "7.0.3",
|
|
19
|
-
"react-redux": "8.
|
|
19
|
+
"react-redux": "8.1.3",
|
|
20
20
|
"react-string-replace": "1.1.1",
|
|
21
21
|
"redis": "4.5.1"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@types/react-redux": "7.1.
|
|
24
|
+
"@types/react-redux": "7.1.30"
|
|
25
25
|
}
|
|
26
26
|
}
|
|
@@ -179,13 +179,19 @@ export const contextListMiddleware: Middleware = ({
|
|
|
179
179
|
: `/${currentLocale}${redirectUrl}`;
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
+
const urlObj = new URL(url, window.location.origin);
|
|
183
|
+
urlObj.searchParams.set('t', new Date().getTime().toString());
|
|
184
|
+
|
|
182
185
|
if (
|
|
183
|
-
isMobileApp ||
|
|
184
|
-
|
|
186
|
+
(isMobileApp ||
|
|
187
|
+
/iPad|iPhone|iPod|Android/i.test(navigator.userAgent)) &&
|
|
188
|
+
!settings.checkout?.iframeExcludedPaymentOptions?.includes(
|
|
189
|
+
result.payload?.pre_order?.payment_option?.slug
|
|
190
|
+
)
|
|
185
191
|
) {
|
|
186
|
-
showMobile3dIframe(
|
|
192
|
+
showMobile3dIframe(urlObj.toString());
|
|
187
193
|
} else {
|
|
188
|
-
window.location.href =
|
|
194
|
+
window.location.href = urlObj.toString();
|
|
189
195
|
}
|
|
190
196
|
|
|
191
197
|
return;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Product } from './product';
|
|
2
|
+
|
|
3
|
+
export type Division = {
|
|
4
|
+
id: number;
|
|
5
|
+
name: string;
|
|
6
|
+
division_type: string;
|
|
7
|
+
parent: number;
|
|
8
|
+
erp_code: string;
|
|
9
|
+
country_id: number;
|
|
10
|
+
city_id: number;
|
|
11
|
+
township_id: number;
|
|
12
|
+
district_id: number;
|
|
13
|
+
address: string;
|
|
14
|
+
phone_number: string;
|
|
15
|
+
fax_number: string;
|
|
16
|
+
is_active: true;
|
|
17
|
+
latitude: null;
|
|
18
|
+
longitude: null;
|
|
19
|
+
monthly_order_limit: string;
|
|
20
|
+
current_balance: string;
|
|
21
|
+
current_balance_last_update: null | string;
|
|
22
|
+
extra_data: object;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
interface ProductB2b extends Product {
|
|
26
|
+
asorti: string;
|
|
27
|
+
category: string;
|
|
28
|
+
currency: string;
|
|
29
|
+
variants: {
|
|
30
|
+
[key: string]: any;
|
|
31
|
+
};
|
|
32
|
+
product_image: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type BasketResponse = {
|
|
36
|
+
id: number;
|
|
37
|
+
total_amount: string;
|
|
38
|
+
total_quantity: number;
|
|
39
|
+
basket_items: [
|
|
40
|
+
{
|
|
41
|
+
total_amount: string;
|
|
42
|
+
price: string;
|
|
43
|
+
quantity: number;
|
|
44
|
+
divisions: Division[];
|
|
45
|
+
product: ProductB2b;
|
|
46
|
+
}
|
|
47
|
+
];
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export type BasketParams = {
|
|
51
|
+
division: string;
|
|
52
|
+
product_remote_id: string;
|
|
53
|
+
quantity: string;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export interface GetResponse<T> {
|
|
57
|
+
count: number;
|
|
58
|
+
next: null;
|
|
59
|
+
previous: null;
|
|
60
|
+
results: T[];
|
|
61
|
+
}
|
package/types/commerce/index.ts
CHANGED
package/types/commerce/order.ts
CHANGED
|
@@ -106,3 +106,14 @@ export interface Order {
|
|
|
106
106
|
tracking_number: string;
|
|
107
107
|
tracking_url: string;
|
|
108
108
|
}
|
|
109
|
+
|
|
110
|
+
export interface Quotations {
|
|
111
|
+
id: number;
|
|
112
|
+
name: string;
|
|
113
|
+
net_amount: string;
|
|
114
|
+
number: string;
|
|
115
|
+
status: string;
|
|
116
|
+
total_amount: string;
|
|
117
|
+
modified_at: string;
|
|
118
|
+
created_at: string;
|
|
119
|
+
}
|
package/types/index.ts
CHANGED
|
@@ -168,6 +168,12 @@ export interface Settings {
|
|
|
168
168
|
source: string;
|
|
169
169
|
destination: string;
|
|
170
170
|
}>;
|
|
171
|
+
checkout?: {
|
|
172
|
+
/**
|
|
173
|
+
* If there is a need to exclude certain payment options from the iframe, for instance, due to CORS issues, this option can be utilized by passing the slugs associated with the desired payment options.
|
|
174
|
+
*/
|
|
175
|
+
iframeExcludedPaymentOptions?: string[];
|
|
176
|
+
};
|
|
171
177
|
}
|
|
172
178
|
|
|
173
179
|
export interface CacheOptions {
|
package/utils/app-fetch.ts
CHANGED
|
@@ -58,7 +58,9 @@ const appFetch = async <T>(
|
|
|
58
58
|
|
|
59
59
|
logger.trace(`FETCH RESPONSE`, { url, response, ip });
|
|
60
60
|
} catch (error) {
|
|
61
|
-
|
|
61
|
+
if (!url.toString().includes('/cms/seo/')) {
|
|
62
|
+
logger.error(`FETCH FAILED`, { url, status, error, ip });
|
|
63
|
+
}
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
return response;
|