@akinon/next 1.8.0 → 1.10.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 +20 -0
- package/api/client.ts +31 -14
- package/data/client/account.ts +21 -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 -75
- 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,25 @@
|
|
|
1
1
|
# @akinon/next
|
|
2
2
|
|
|
3
|
+
## 1.10.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- ZERO-2330: Fix logout endpoint
|
|
8
|
+
- ZERO-2327: Add service for bulk cancellation
|
|
9
|
+
|
|
10
|
+
## 1.9.0
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- ZERO-2297: Add landing page structure
|
|
15
|
+
- ZERO-2317: Prevent browser cache using timestamp
|
|
16
|
+
- ZERO-2293: Upgrade Redux
|
|
17
|
+
- ZERO-2285: Allow custom headers in server data fetch functions
|
|
18
|
+
- ZERO-1577: Add option to exclude payment options from iframe
|
|
19
|
+
- ZERO-2282: Allow custom headers in client proxy API
|
|
20
|
+
- ZERO-2308: Add B2B endpoints and types
|
|
21
|
+
- ZERO-2202: Ensure commerce endpoints end with trailing slash
|
|
22
|
+
|
|
3
23
|
## 1.8.0
|
|
4
24
|
|
|
5
25
|
### 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, {
|
|
@@ -121,6 +130,15 @@ const accountApi = api.injectEndpoints({
|
|
|
121
130
|
body
|
|
122
131
|
})
|
|
123
132
|
}),
|
|
133
|
+
bulkCancellation: builder.mutation<void, AccountOrderCancellation>({
|
|
134
|
+
query: (body) => ({
|
|
135
|
+
url: buildClientRequestUrl(account.bulkCancellationRequest, {
|
|
136
|
+
contentType: 'application/json'
|
|
137
|
+
}),
|
|
138
|
+
method: 'POST',
|
|
139
|
+
body
|
|
140
|
+
})
|
|
141
|
+
}),
|
|
124
142
|
getCancellationReasons: builder.query<AccountOrderCancellationReason, void>(
|
|
125
143
|
{
|
|
126
144
|
query: () => buildClientRequestUrl(account.cancellationReasons)
|
|
@@ -170,12 +188,14 @@ export const {
|
|
|
170
188
|
useGetContactSubjectsQuery,
|
|
171
189
|
useGetOrderQuery,
|
|
172
190
|
useGetOrdersQuery,
|
|
191
|
+
useGetQuotationsQuery,
|
|
173
192
|
useSendContactMutation,
|
|
174
193
|
useUpdateEmailMutation,
|
|
175
194
|
useUpdatePasswordMutation,
|
|
176
195
|
useUpdateProfileMutation,
|
|
177
196
|
useGetProfileInfoQuery,
|
|
178
197
|
useCancelOrderMutation,
|
|
198
|
+
useBulkCancellationMutation,
|
|
179
199
|
useGetCancellationReasonsQuery,
|
|
180
200
|
useGetBasketOffersQuery,
|
|
181
201
|
useGetFutureBasketOffersQuery,
|
|
@@ -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,18 @@
|
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
order: '/users/orders/',
|
|
8
|
+
orderId: (id: string | string[]) => `/users/orders/${id}/`,
|
|
9
|
+
cancelOrder: (id: string) => `/orders/${id}/cancel/`,
|
|
10
|
+
bulkCancellationRequest: '/users/orders/bulk_cancellation_requests/',
|
|
11
|
+
cancellationReasons: '/users/orders/cancellation_reasons/',
|
|
12
|
+
updatePassword: '/users/password/change/',
|
|
13
|
+
updateEmail: '/users/email-change/',
|
|
14
|
+
updateProfile: '/users/profile/',
|
|
15
|
+
getContactSubjects: '/users/contact-us-subjects/',
|
|
14
16
|
getOrders: ({
|
|
15
17
|
page,
|
|
16
18
|
limit,
|
|
@@ -20,9 +22,10 @@ export const account = {
|
|
|
20
22
|
limit?: number;
|
|
21
23
|
createdDate?: string;
|
|
22
24
|
}) =>
|
|
23
|
-
`/users/orders
|
|
25
|
+
`/users/orders/?page=${page || 1}&limit=${limit || 12} ${
|
|
24
26
|
createdDate ? `&created_date__gte=${createdDate}` : ''
|
|
25
27
|
}`,
|
|
28
|
+
getQuotations: '/b2b/my-quotations',
|
|
26
29
|
sendContact: '/users/contact-us',
|
|
27
30
|
passwordReset: (slug: string) => `/users/reset/${slug}`,
|
|
28
31
|
anonymize: '/users/anonymize/',
|
|
@@ -31,95 +34,100 @@ export const account = {
|
|
|
31
34
|
};
|
|
32
35
|
|
|
33
36
|
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
|
|
37
|
+
base: '/address/',
|
|
38
|
+
countries: '/address/country/',
|
|
39
|
+
getAddresses: '/address/detailed/',
|
|
40
|
+
getCities: (country: string) => `/address/city/?country=${country}`,
|
|
41
|
+
getTownships: (city: string) => `/address/township/?city=${city}`,
|
|
42
|
+
getDistricts: (township: string) => `/address/district/?township=${township}`,
|
|
43
|
+
getRetailStore: '/address/city/?retailstore__isnull=false',
|
|
41
44
|
getRetailStoreCities: (country: string) =>
|
|
42
|
-
`/address/city
|
|
45
|
+
`/address/city/?retailstore__isnull=false&country=${country}`,
|
|
43
46
|
getRetailStoreTownships: (city: string) =>
|
|
44
|
-
`/address/township
|
|
45
|
-
editAddress: (pk: number) => `/address/${pk}
|
|
46
|
-
removeAddress: (id: number) => `/address/${id}
|
|
47
|
-
setDefaultAddress: (pk: number) => `/address/${pk}
|
|
47
|
+
`/address/township/?retailstore__isnull=false&city=${city}`,
|
|
48
|
+
editAddress: (pk: number) => `/address/${pk}/`,
|
|
49
|
+
removeAddress: (id: number) => `/address/${id}/`,
|
|
50
|
+
setDefaultAddress: (pk: number) => `/address/${pk}/`
|
|
48
51
|
};
|
|
49
52
|
|
|
50
53
|
export const basket = {
|
|
51
|
-
getBasket: '/baskets/basket'
|
|
54
|
+
getBasket: '/baskets/basket/'
|
|
52
55
|
};
|
|
53
56
|
|
|
54
57
|
export const category = {
|
|
55
|
-
// eslint-disable-next-line projectzero/client-url
|
|
56
58
|
list: '/list/',
|
|
57
59
|
getCategoryByPk: (pk: number) => `/category/${pk}/`,
|
|
58
|
-
getCategoryBySlug: (slug: string) => `/${slug}
|
|
60
|
+
getCategoryBySlug: (slug: string) => `/${slug}/`,
|
|
59
61
|
getSpecialPageByPk: (pk: number) => `/special-page/${pk}/`
|
|
60
62
|
};
|
|
61
63
|
|
|
62
64
|
export const checkout = {
|
|
63
|
-
get3dRedirectForm: '/orders/redirect-three-d',
|
|
64
|
-
fetchCheckout: '/orders/checkout',
|
|
65
|
+
get3dRedirectForm: '/orders/redirect-three-d/',
|
|
66
|
+
fetchCheckout: '/orders/checkout/',
|
|
65
67
|
fetchCheckoutResult: (token: string) =>
|
|
66
|
-
`/orders/completed/${token}
|
|
67
|
-
getContract: (slug: string) => `/orders/contract
|
|
68
|
-
completeCreditCardPayment:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
68
|
+
`/orders/completed/${token}/?format=json`,
|
|
69
|
+
getContract: (slug: string) => `/orders/contract/?contract_name=${slug}`,
|
|
70
|
+
completeCreditCardPayment:
|
|
71
|
+
'/orders/checkout/?page=CreditCardConfirmationPage',
|
|
72
|
+
completeFundsTransfer: '/orders/checkout/?page=FundsTransferPage',
|
|
73
|
+
guestLogin: '/orders/checkout/?page=IndexPage',
|
|
74
|
+
setDeliveryOption: '/orders/checkout/?page=DeliveryOptionSelectionPage',
|
|
75
|
+
setAddresses: '/orders/checkout/?page=AddressSelectionPage',
|
|
76
|
+
setShippingOption: '/orders/checkout/?page=ShippingOptionSelectionPage',
|
|
77
|
+
setPaymentOption: '/orders/checkout/?page=PaymentOptionSelectionPage',
|
|
78
|
+
setBinNumber: '/orders/checkout/?page=BinNumberPage',
|
|
79
|
+
setMasterpassBinNumber: '/orders/checkout/?page=MasterpassBinNumberPage',
|
|
80
|
+
setInstallmentOption: '/orders/checkout/?page=InstallmentSelectionPage',
|
|
78
81
|
setMasterPassInstallmentOption:
|
|
79
|
-
'/orders/checkout
|
|
80
|
-
setFundsTransferOption: '/orders/checkout
|
|
81
|
-
getCheckoutProviders: '/orders/checkout
|
|
82
|
-
|
|
82
|
+
'/orders/checkout/?page=MasterpassInstallmentPage',
|
|
83
|
+
setFundsTransferOption: '/orders/checkout/?page=FundsTransferChoicePage',
|
|
84
|
+
getCheckoutProviders: '/orders/checkout-provider-list/',
|
|
85
|
+
getCheckoutProvidersIndex: '/orders/checkout/?page=CheckoutProviderIndexPage',
|
|
86
|
+
setCheckoutProvider: '/orders/checkout/?page=CheckoutProviderSelectionPage',
|
|
83
87
|
completeRedirectionPayment:
|
|
84
|
-
'/orders/checkout
|
|
88
|
+
'/orders/checkout/?page=RedirectionPaymentSelectedPage',
|
|
85
89
|
confirmationPaymentSelect:
|
|
86
|
-
'/orders/checkout
|
|
87
|
-
confirmationQuery: '/orders/checkout
|
|
88
|
-
confirmationComplete:
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
'/orders/checkout/?page=ConfirmationPaymentSelectedPage',
|
|
91
|
+
confirmationQuery: '/orders/checkout/?page=ConfirmationPaymentQueryPage',
|
|
92
|
+
confirmationComplete:
|
|
93
|
+
'/orders/checkout/?page=ConfirmationPaymentCompletePage',
|
|
94
|
+
completeLoyaltyPayment: '/orders/checkout/?page=LoyaltyPaymentSelectedPage',
|
|
95
|
+
loyaltyMoneyUsage: '/orders/checkout/?page=LoyaltyMoneyUsagePage',
|
|
96
|
+
setOrderNote: '/orders/checkout/?page=OrderNotePage'
|
|
92
97
|
};
|
|
93
98
|
|
|
94
99
|
export const flatpage = {
|
|
95
100
|
getFlatPageByPk: (pk: number) => `/shop-flat-page/${pk}/`
|
|
96
101
|
};
|
|
97
102
|
|
|
103
|
+
export const landingpage = {
|
|
104
|
+
getLandingPageByPk: (pk: number) => `/landing-page/${pk}/`
|
|
105
|
+
};
|
|
106
|
+
|
|
98
107
|
export const misc = {
|
|
99
|
-
autocomplete: '/autocomplete',
|
|
100
|
-
stores: '/stores',
|
|
101
|
-
menu: '/misc/menus/generate',
|
|
108
|
+
autocomplete: '/autocomplete/',
|
|
109
|
+
stores: '/stores/',
|
|
110
|
+
menu: '/misc/menus/generate/',
|
|
102
111
|
prettyUrl: (pathname: string) => `/misc/pretty-url${pathname}/`,
|
|
103
112
|
prettyUrls: (pathname: string) => `/pretty_urls/?new_path__exact=${pathname}`,
|
|
104
|
-
emailSubscription: '/email-subscription',
|
|
113
|
+
emailSubscription: '/email-subscription/',
|
|
105
114
|
menus: (depthHeight: number, parent?: string) =>
|
|
106
115
|
`/menus/generate/?depth_height=${depthHeight}${
|
|
107
116
|
parent ? `&parent=${parent}` : ''
|
|
108
117
|
}`,
|
|
109
|
-
// eslint-disable-next-line projectzero/client-url
|
|
110
118
|
cmsSeo: (slug: string | string[]) => `/cms/seo/?url=${slug ? slug : '/'}`,
|
|
111
|
-
setCurrency: '/users/activate-currency'
|
|
119
|
+
setCurrency: '/users/activate-currency/'
|
|
112
120
|
};
|
|
113
121
|
|
|
114
122
|
export const product = {
|
|
115
123
|
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}
|
|
124
|
+
installments: (productId: number) => `/payments/cards/product/${productId}/`,
|
|
125
|
+
getProductByPk: (pk: number) => `/product/${pk}/`,
|
|
126
|
+
getGroupProductByPk: (pk: number) => `/group-product/${pk}/`,
|
|
119
127
|
getRetailStoreStock: (productPk: string, queryString: string) =>
|
|
120
|
-
`/retail_store_stock/${productPk}
|
|
121
|
-
addProduct: '/baskets/basket',
|
|
122
|
-
slug: (slug: string) => `/${slug}
|
|
128
|
+
`/retail_store_stock/${productPk}/?${queryString}`,
|
|
129
|
+
addProduct: '/baskets/basket/',
|
|
130
|
+
slug: (slug: string) => `/${slug}/`,
|
|
123
131
|
categoryUrl: (pk: number) => `/products/${pk}/category_nodes/?limit=1`,
|
|
124
132
|
breadcrumbUrl: (menuitemmodel: string) =>
|
|
125
133
|
`/menus/generate_breadcrumb/?item=${menuitemmodel}&generator_name=menu_item`
|
|
@@ -127,28 +135,38 @@ export const product = {
|
|
|
127
135
|
|
|
128
136
|
export const wishlist = {
|
|
129
137
|
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'
|
|
138
|
+
`/wishlists/favourite-products/?limit=${limit || 12}&page=${page || 1}`,
|
|
139
|
+
addFavorite: '/wishlists/favourite-products/',
|
|
140
|
+
removeFavorite: (favPk: number) => `/wishlists/favourite-products/${favPk}/`,
|
|
141
|
+
addStockAlert: '/wishlists/product-alerts/'
|
|
134
142
|
};
|
|
135
143
|
|
|
136
144
|
export const user = {
|
|
137
|
-
currentUser: '/current_user',
|
|
138
|
-
login: '/users/login',
|
|
139
|
-
// eslint-disable-next-line projectzero/client-url
|
|
145
|
+
currentUser: '/current_user/',
|
|
146
|
+
login: '/users/login/',
|
|
140
147
|
register: '/users/registration/',
|
|
141
148
|
logout: '/users/logout',
|
|
142
|
-
captcha: '/users/pz-captcha',
|
|
143
|
-
profiles: '/users/profile',
|
|
144
|
-
forgotPassword: '/users/password/reset',
|
|
145
|
-
changeEmailVerification: (token: string) =>
|
|
146
|
-
|
|
147
|
-
|
|
149
|
+
captcha: '/users/pz-captcha/',
|
|
150
|
+
profiles: '/users/profile/',
|
|
151
|
+
forgotPassword: '/users/password/reset/',
|
|
152
|
+
changeEmailVerification: (token: string) =>
|
|
153
|
+
`/users/email-set-primary/${token}/`,
|
|
154
|
+
confirmEmailVerification: (token: string) =>
|
|
155
|
+
`/users/registration/account-confirm-email/${token}/`,
|
|
156
|
+
csrfToken: '/csrf_token/'
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
export const b2b = {
|
|
160
|
+
basket: '/b2b/basket/',
|
|
161
|
+
saveBasket: '/b2b/basket/save/',
|
|
162
|
+
draftBaskets: '/b2b/basket/drafts/',
|
|
163
|
+
divisions: '/b2b/my-divisions/',
|
|
164
|
+
myQuotations: '/b2b/my-quotations/',
|
|
165
|
+
loadBasket: (id) => `/b2b/basket/${id}/load/`
|
|
148
166
|
};
|
|
149
167
|
|
|
150
168
|
export const widgets = {
|
|
151
|
-
|
|
169
|
+
getWidget: (slug: string) => `/widgets/${slug}/`
|
|
152
170
|
};
|
|
153
171
|
|
|
154
172
|
const URLS = {
|
|
@@ -166,6 +184,7 @@ const URLS = {
|
|
|
166
184
|
};
|
|
167
185
|
|
|
168
186
|
const UrlProxyHandler = {
|
|
187
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
169
188
|
get(target, prop, receiver) {
|
|
170
189
|
if (typeof target[prop] === 'function') {
|
|
171
190
|
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.10.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;
|