@akinon/next 1.9.0 → 1.11.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 +19 -0
- package/components/icon.tsx +18 -0
- package/components/loader-spinner.tsx +23 -0
- package/components/radio.tsx +18 -0
- package/data/client/account.ts +10 -0
- package/data/client/api.ts +51 -9
- package/data/client/b2b.ts +67 -5
- package/data/server/form.ts +22 -0
- package/data/server/index.ts +1 -0
- package/data/urls.ts +8 -2
- package/lib/cache.ts +2 -1
- package/middlewares/redirection-payment.ts +23 -6
- package/middlewares/three-d-redirection.ts +23 -1
- package/package.json +1 -1
- package/types/commerce/b2b.ts +53 -1
- package/types/commerce/form.ts +66 -0
- package/types/commerce/index.ts +1 -0
- package/types/index.ts +8 -0
- package/utils/index.ts +12 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @akinon/next
|
|
2
2
|
|
|
3
|
+
## 1.11.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- ZERO-2355: Add LoaderSpinner component
|
|
8
|
+
- ZERO-2305: Add endpoints for B2B Basket
|
|
9
|
+
- ZERO-2319: Show 3D & redirection payment errors
|
|
10
|
+
- ZERO-2353: Add Icon component
|
|
11
|
+
- ZERO-2357: Add Radio component
|
|
12
|
+
- ZERO-2307: Prevent multiple mutation calls
|
|
13
|
+
- ZERO-2240: Add endpoints and types for dynamic forms
|
|
14
|
+
|
|
15
|
+
## 1.10.0
|
|
16
|
+
|
|
17
|
+
### Minor Changes
|
|
18
|
+
|
|
19
|
+
- ZERO-2330: Fix logout endpoint
|
|
20
|
+
- ZERO-2327: Add service for bulk cancellation
|
|
21
|
+
|
|
3
22
|
## 1.9.0
|
|
4
23
|
|
|
5
24
|
### Minor Changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { IconProps } from '../types/index';
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
|
|
4
|
+
export const Icon = (props: IconProps) => {
|
|
5
|
+
const { name, size, className, ...rest } = props;
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<i
|
|
9
|
+
className={clsx(`flex pz-icon-${name}`, className)}
|
|
10
|
+
{...rest}
|
|
11
|
+
style={
|
|
12
|
+
size && {
|
|
13
|
+
fontSize: `${size}px`
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/>
|
|
17
|
+
);
|
|
18
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { twMerge } from 'tailwind-merge';
|
|
2
|
+
|
|
3
|
+
type LoaderSpinnerProps = {
|
|
4
|
+
className?: string;
|
|
5
|
+
borderType?: 'solid' | 'dotted' | 'dashed';
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const LoaderSpinner: React.FC<LoaderSpinnerProps> = ({
|
|
9
|
+
borderType = 'solid',
|
|
10
|
+
className
|
|
11
|
+
}) => {
|
|
12
|
+
return (
|
|
13
|
+
<div className="w-full h-full flex justify-center items-center">
|
|
14
|
+
<div
|
|
15
|
+
className={twMerge(
|
|
16
|
+
'w-8 h-8 border-2 border-primary border-t-transparent rounded-full animate-spin',
|
|
17
|
+
`border-${borderType}`,
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
/>
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { forwardRef } from 'react';
|
|
2
|
+
import { RadioProps } from '../types/index';
|
|
3
|
+
import { twMerge } from 'tailwind-merge';
|
|
4
|
+
|
|
5
|
+
const Radio = forwardRef<HTMLInputElement, RadioProps>((props, ref) => {
|
|
6
|
+
const { children, ...rest } = props;
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<label className={twMerge('flex items-center text-xs', props.className)}>
|
|
10
|
+
<input type="radio" {...rest} ref={ref} className="w-4 h-4" />
|
|
11
|
+
{children && <span className="text-xs ml-2">{children}</span>}
|
|
12
|
+
</label>
|
|
13
|
+
);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
Radio.displayName = 'Radio';
|
|
17
|
+
|
|
18
|
+
export { Radio };
|
package/data/client/account.ts
CHANGED
|
@@ -130,6 +130,15 @@ const accountApi = api.injectEndpoints({
|
|
|
130
130
|
body
|
|
131
131
|
})
|
|
132
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
|
+
}),
|
|
133
142
|
getCancellationReasons: builder.query<AccountOrderCancellationReason, void>(
|
|
134
143
|
{
|
|
135
144
|
query: () => buildClientRequestUrl(account.cancellationReasons)
|
|
@@ -186,6 +195,7 @@ export const {
|
|
|
186
195
|
useUpdateProfileMutation,
|
|
187
196
|
useGetProfileInfoQuery,
|
|
188
197
|
useCancelOrderMutation,
|
|
198
|
+
useBulkCancellationMutation,
|
|
189
199
|
useGetCancellationReasonsQuery,
|
|
190
200
|
useGetBasketOffersQuery,
|
|
191
201
|
useGetFutureBasketOffersQuery,
|
package/data/client/api.ts
CHANGED
|
@@ -1,10 +1,42 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
createApi,
|
|
3
|
+
fetchBaseQuery,
|
|
4
|
+
retry,
|
|
5
|
+
BaseQueryFn,
|
|
6
|
+
FetchBaseQueryError,
|
|
7
|
+
FetchBaseQueryMeta,
|
|
8
|
+
FetchArgs,
|
|
9
|
+
BaseQueryApi
|
|
10
|
+
} from '@reduxjs/toolkit/query/react';
|
|
2
11
|
import settings from 'settings';
|
|
3
12
|
import { getCookie } from '../../utils';
|
|
13
|
+
import { RootState } from '@theme/redux/store';
|
|
4
14
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
15
|
+
interface CustomBaseQueryApi extends BaseQueryApi {
|
|
16
|
+
getState: () => RootState;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const customBaseQuery: BaseQueryFn<
|
|
20
|
+
string | FetchArgs,
|
|
21
|
+
unknown,
|
|
22
|
+
FetchBaseQueryError,
|
|
23
|
+
{},
|
|
24
|
+
FetchBaseQueryMeta
|
|
25
|
+
> = async (args, api: CustomBaseQueryApi, extraOptions) => {
|
|
26
|
+
const mutations = Object.entries(api.getState()?.api?.mutations ?? {}).map(
|
|
27
|
+
(x) => x[1]
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
if (
|
|
31
|
+
api.type === 'mutation' &&
|
|
32
|
+
mutations.filter(
|
|
33
|
+
(m) => m.status === 'pending' && m.endpointName === api.endpoint
|
|
34
|
+
).length > 1
|
|
35
|
+
) {
|
|
36
|
+
api.abort('Mutation already in progress.');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const baseQuery = fetchBaseQuery({
|
|
8
40
|
prepareHeaders: async (headers) => {
|
|
9
41
|
const csrfCookie = getCookie('csrftoken');
|
|
10
42
|
const activeLocale = getCookie('pz-locale');
|
|
@@ -19,22 +51,32 @@ export const api = createApi({
|
|
|
19
51
|
if (csrfCookie) {
|
|
20
52
|
headers.set('x-csrftoken', `${csrfCookie}`);
|
|
21
53
|
}
|
|
22
|
-
|
|
23
54
|
return headers;
|
|
24
55
|
},
|
|
25
56
|
credentials: 'include'
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
|
|
57
|
+
});
|
|
58
|
+
try {
|
|
59
|
+
const result = await baseQuery(args, api, extraOptions);
|
|
60
|
+
return result;
|
|
61
|
+
} catch (error) {
|
|
62
|
+
return { error };
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const api = createApi({
|
|
67
|
+
reducerPath: 'api',
|
|
68
|
+
baseQuery: retry(customBaseQuery, { maxRetries: 3 }),
|
|
29
69
|
tagTypes: [
|
|
30
70
|
'Basket',
|
|
71
|
+
'BasketB2b',
|
|
72
|
+
'DraftsB2b',
|
|
31
73
|
'Product',
|
|
32
74
|
'Checkout',
|
|
33
75
|
'Favorite',
|
|
34
76
|
'Addresses',
|
|
35
77
|
'Profile'
|
|
36
78
|
],
|
|
37
|
-
endpoints: () => ({})
|
|
79
|
+
endpoints: () => ({})
|
|
38
80
|
});
|
|
39
81
|
|
|
40
82
|
export const {
|
package/data/client/b2b.ts
CHANGED
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
import { buildClientRequestUrl } from '../../utils';
|
|
2
2
|
import { api } from './api';
|
|
3
3
|
import { b2b } from '../urls';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
Basket,
|
|
6
|
+
BasketParams,
|
|
7
|
+
BasketResponse,
|
|
8
|
+
Division,
|
|
9
|
+
DraftResponse,
|
|
10
|
+
GetResponse,
|
|
11
|
+
LoadBasketParams,
|
|
12
|
+
SaveBasketParams,
|
|
13
|
+
updateProduct,
|
|
14
|
+
CreateQuotation
|
|
15
|
+
} from '../../types';
|
|
5
16
|
|
|
6
17
|
const b2bApi = api.injectEndpoints({
|
|
7
18
|
endpoints: (build) => ({
|
|
8
|
-
|
|
19
|
+
getBasketB2b: build.query<BasketResponse, void>({
|
|
9
20
|
query: () =>
|
|
10
21
|
buildClientRequestUrl(b2b.basket, {
|
|
11
22
|
contentType: 'application/json'
|
|
12
23
|
}),
|
|
13
|
-
|
|
14
|
-
providesTags: ['Basket']
|
|
24
|
+
providesTags: ['BasketB2b']
|
|
15
25
|
}),
|
|
16
26
|
getDivisions: build.query<GetResponse<Division>, void>({
|
|
17
27
|
query: () => buildClientRequestUrl(b2b.divisions)
|
|
@@ -25,8 +35,60 @@ const b2bApi = api.injectEndpoints({
|
|
|
25
35
|
body
|
|
26
36
|
})
|
|
27
37
|
}),
|
|
38
|
+
saveBasket: build.mutation<BasketResponse, SaveBasketParams>({
|
|
39
|
+
query: (body) => ({
|
|
40
|
+
url: buildClientRequestUrl(b2b.saveBasket, {
|
|
41
|
+
contentType: 'application/json'
|
|
42
|
+
}),
|
|
43
|
+
method: 'POST',
|
|
44
|
+
body
|
|
45
|
+
}),
|
|
46
|
+
invalidatesTags: ['BasketB2b', 'DraftsB2b']
|
|
47
|
+
}),
|
|
48
|
+
getDrafts: build.query<DraftResponse[], void>({
|
|
49
|
+
query: () => buildClientRequestUrl(b2b.draftBaskets),
|
|
50
|
+
providesTags: ['DraftsB2b']
|
|
51
|
+
}),
|
|
52
|
+
loadBasket: build.mutation<string, number>({
|
|
53
|
+
query: (id) => ({
|
|
54
|
+
url: buildClientRequestUrl(b2b.loadBasket(id), {
|
|
55
|
+
contentType: 'application/json'
|
|
56
|
+
}),
|
|
57
|
+
method: 'POST'
|
|
58
|
+
}),
|
|
59
|
+
invalidatesTags: ['BasketB2b']
|
|
60
|
+
}),
|
|
61
|
+
updateProduct: build.mutation<Basket, updateProduct>({
|
|
62
|
+
query: (body) => ({
|
|
63
|
+
url: buildClientRequestUrl(b2b.basket, {
|
|
64
|
+
contentType: 'application/json'
|
|
65
|
+
}),
|
|
66
|
+
method: 'PUT',
|
|
67
|
+
body
|
|
68
|
+
}),
|
|
69
|
+
invalidatesTags: ['BasketB2b']
|
|
70
|
+
}),
|
|
71
|
+
createQuotation: build.mutation<BasketResponse, CreateQuotation>({
|
|
72
|
+
query: (body) => ({
|
|
73
|
+
url: buildClientRequestUrl(b2b.myQuotations, {
|
|
74
|
+
contentType: 'application/json'
|
|
75
|
+
}),
|
|
76
|
+
method: 'POST',
|
|
77
|
+
body
|
|
78
|
+
}),
|
|
79
|
+
invalidatesTags: ['BasketB2b', 'DraftsB2b']
|
|
80
|
+
}),
|
|
28
81
|
}),
|
|
29
82
|
overrideExisting: true
|
|
30
83
|
});
|
|
31
84
|
|
|
32
|
-
export const {
|
|
85
|
+
export const {
|
|
86
|
+
useGetBasketB2bQuery,
|
|
87
|
+
useLazyGetDivisionsQuery,
|
|
88
|
+
useAddToBasketMutation,
|
|
89
|
+
useSaveBasketMutation,
|
|
90
|
+
useGetDraftsQuery,
|
|
91
|
+
useLoadBasketMutation,
|
|
92
|
+
useUpdateProductMutation,
|
|
93
|
+
useCreateQuotationMutation
|
|
94
|
+
} = b2bApi;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Cache, CacheKey } from "../../lib/cache";
|
|
2
|
+
import { FormType } from "../../types/commerce/form";
|
|
3
|
+
|
|
4
|
+
import appFetch from "../../utils/app-fetch";
|
|
5
|
+
import { form } from "../urls";
|
|
6
|
+
|
|
7
|
+
const getFormDataHandler = (pk: number) => {
|
|
8
|
+
return async function () {
|
|
9
|
+
const data = await appFetch<FormType>(form.getForm(pk), {
|
|
10
|
+
headers: {
|
|
11
|
+
Accept: 'application/json',
|
|
12
|
+
'Content-Type': 'application/json'
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
return data;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const getFormData = ({ pk }: { pk: number }) => {
|
|
21
|
+
return Cache.wrap(CacheKey.Form(pk), getFormDataHandler(pk));
|
|
22
|
+
};
|
package/data/server/index.ts
CHANGED
package/data/urls.ts
CHANGED
|
@@ -7,6 +7,7 @@ export const account = {
|
|
|
7
7
|
order: '/users/orders/',
|
|
8
8
|
orderId: (id: string | string[]) => `/users/orders/${id}/`,
|
|
9
9
|
cancelOrder: (id: string) => `/orders/${id}/cancel/`,
|
|
10
|
+
bulkCancellationRequest: '/users/orders/bulk_cancellation_requests/',
|
|
10
11
|
cancellationReasons: '/users/orders/cancellation_reasons/',
|
|
11
12
|
updatePassword: '/users/password/change/',
|
|
12
13
|
updateEmail: '/users/email-change/',
|
|
@@ -144,7 +145,7 @@ export const user = {
|
|
|
144
145
|
currentUser: '/current_user/',
|
|
145
146
|
login: '/users/login/',
|
|
146
147
|
register: '/users/registration/',
|
|
147
|
-
logout: '/users/logout
|
|
148
|
+
logout: '/users/logout',
|
|
148
149
|
captcha: '/users/pz-captcha/',
|
|
149
150
|
profiles: '/users/profile/',
|
|
150
151
|
forgotPassword: '/users/password/reset/',
|
|
@@ -168,6 +169,10 @@ export const widgets = {
|
|
|
168
169
|
getWidget: (slug: string) => `/widgets/${slug}/`
|
|
169
170
|
};
|
|
170
171
|
|
|
172
|
+
export const form = {
|
|
173
|
+
getForm: (pk: number) => `/forms/${pk}/generate/`,
|
|
174
|
+
};
|
|
175
|
+
|
|
171
176
|
const URLS = {
|
|
172
177
|
account,
|
|
173
178
|
address,
|
|
@@ -179,7 +184,8 @@ const URLS = {
|
|
|
179
184
|
product,
|
|
180
185
|
wishlist,
|
|
181
186
|
user,
|
|
182
|
-
widgets
|
|
187
|
+
widgets,
|
|
188
|
+
form
|
|
183
189
|
};
|
|
184
190
|
|
|
185
191
|
const UrlProxyHandler = {
|
package/lib/cache.ts
CHANGED
|
@@ -51,7 +51,8 @@ export const CacheKey = {
|
|
|
51
51
|
Menu: (depth: number, parent?: string) =>
|
|
52
52
|
`menu_${depth}${parent ? `_${parent}` : ''}`,
|
|
53
53
|
Seo: (url: string) => `seo_${url}`,
|
|
54
|
-
RootSeo: 'root_seo'
|
|
54
|
+
RootSeo: 'root_seo',
|
|
55
|
+
Form: (pk: number) => `form_${pk}`
|
|
55
56
|
};
|
|
56
57
|
|
|
57
58
|
export class Cache {
|
|
@@ -4,7 +4,6 @@ import Settings from 'settings';
|
|
|
4
4
|
import logger from '../utils/log';
|
|
5
5
|
import { PzNextRequest } from '.';
|
|
6
6
|
import { getUrlPathWithLocale } from '../utils/localization';
|
|
7
|
-
import { urlLocaleMatcherRegex } from '../utils';
|
|
8
7
|
|
|
9
8
|
const streamToString = async (stream: ReadableStream<Uint8Array> | null) => {
|
|
10
9
|
if (stream) {
|
|
@@ -33,10 +32,6 @@ const withRedirectionPayment =
|
|
|
33
32
|
(middleware: NextMiddleware) =>
|
|
34
33
|
async (req: PzNextRequest, event: NextFetchEvent) => {
|
|
35
34
|
const url = req.nextUrl.clone();
|
|
36
|
-
const pathnameWithoutLocale = url.pathname.replace(
|
|
37
|
-
urlLocaleMatcherRegex,
|
|
38
|
-
''
|
|
39
|
-
);
|
|
40
35
|
const searchParams = new URLSearchParams(url.search);
|
|
41
36
|
const ip = req.headers.get('x-forwarded-for') ?? '';
|
|
42
37
|
|
|
@@ -70,12 +65,34 @@ const withRedirectionPayment =
|
|
|
70
65
|
|
|
71
66
|
const response = await request.json();
|
|
72
67
|
|
|
73
|
-
const { context_list: contextList } = response;
|
|
68
|
+
const { context_list: contextList, errors } = response;
|
|
74
69
|
const redirectionContext = contextList?.find(
|
|
75
70
|
(context) => context.page_context?.redirect_url
|
|
76
71
|
);
|
|
77
72
|
const redirectUrl = redirectionContext?.page_context?.redirect_url;
|
|
78
73
|
|
|
74
|
+
if (errors && Object.keys(errors).length) {
|
|
75
|
+
logger.error('Error while completing redirection payment', {
|
|
76
|
+
middleware: 'redirection-payment',
|
|
77
|
+
errors,
|
|
78
|
+
requestHeaders,
|
|
79
|
+
ip
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return NextResponse.redirect(
|
|
83
|
+
`${url.origin}${getUrlPathWithLocale(
|
|
84
|
+
'/orders/checkout/',
|
|
85
|
+
req.cookies.get('pz-locale')?.value
|
|
86
|
+
)}`,
|
|
87
|
+
{
|
|
88
|
+
status: 303,
|
|
89
|
+
headers: {
|
|
90
|
+
'Set-Cookie': `pz-pos-error=${JSON.stringify(errors)}; path=/;`
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
79
96
|
logger.info('Order success page context list', {
|
|
80
97
|
middleware: 'redirection-payment',
|
|
81
98
|
contextList,
|
|
@@ -64,12 +64,34 @@ const withThreeDRedirection =
|
|
|
64
64
|
|
|
65
65
|
const response = await request.json();
|
|
66
66
|
|
|
67
|
-
const { context_list: contextList } = response;
|
|
67
|
+
const { context_list: contextList, errors } = response;
|
|
68
68
|
const redirectionContext = contextList?.find(
|
|
69
69
|
(context) => context.page_context?.redirect_url
|
|
70
70
|
);
|
|
71
71
|
const redirectUrl = redirectionContext?.page_context?.redirect_url;
|
|
72
72
|
|
|
73
|
+
if (errors && Object.keys(errors).length) {
|
|
74
|
+
logger.error('Error while completing 3D payment', {
|
|
75
|
+
middleware: 'three-d-redirection',
|
|
76
|
+
errors,
|
|
77
|
+
requestHeaders,
|
|
78
|
+
ip
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return NextResponse.redirect(
|
|
82
|
+
`${url.origin}${getUrlPathWithLocale(
|
|
83
|
+
'/orders/checkout/',
|
|
84
|
+
req.cookies.get('pz-locale')?.value
|
|
85
|
+
)}`,
|
|
86
|
+
{
|
|
87
|
+
status: 303,
|
|
88
|
+
headers: {
|
|
89
|
+
'Set-Cookie': `pz-pos-error=${JSON.stringify(errors)}; path=/;`
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
73
95
|
logger.info('Order success page context list', {
|
|
74
96
|
middleware: 'three-d-redirection',
|
|
75
97
|
contextList,
|
package/package.json
CHANGED
package/types/commerce/b2b.ts
CHANGED
|
@@ -23,6 +23,10 @@ export type Division = {
|
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
interface ProductB2b extends Product {
|
|
26
|
+
sku: string;
|
|
27
|
+
name: string;
|
|
28
|
+
price: string;
|
|
29
|
+
base_code: string;
|
|
26
30
|
asorti: string;
|
|
27
31
|
category: string;
|
|
28
32
|
currency: string;
|
|
@@ -41,21 +45,69 @@ export type BasketResponse = {
|
|
|
41
45
|
total_amount: string;
|
|
42
46
|
price: string;
|
|
43
47
|
quantity: number;
|
|
44
|
-
divisions:
|
|
48
|
+
divisions: BasketItemDivision[];
|
|
45
49
|
product: ProductB2b;
|
|
50
|
+
product_remote_id: number;
|
|
46
51
|
}
|
|
47
52
|
];
|
|
48
53
|
};
|
|
49
54
|
|
|
55
|
+
export interface QuotationErrorType {
|
|
56
|
+
data: {
|
|
57
|
+
non_field_errors: string[];
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface BasketItemDivision {
|
|
62
|
+
id: number;
|
|
63
|
+
name: string;
|
|
64
|
+
erp_code: string;
|
|
65
|
+
quantity: number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export type BasketItemType = {
|
|
69
|
+
total_amount: string;
|
|
70
|
+
price: string;
|
|
71
|
+
quantity: number;
|
|
72
|
+
divisions: BasketItemDivision[];
|
|
73
|
+
product: ProductB2b;
|
|
74
|
+
product_remote_id: number;
|
|
75
|
+
}
|
|
76
|
+
|
|
50
77
|
export type BasketParams = {
|
|
51
78
|
division: string;
|
|
52
79
|
product_remote_id: string;
|
|
53
80
|
quantity: string;
|
|
54
81
|
};
|
|
55
82
|
|
|
83
|
+
export type SaveBasketParams = {
|
|
84
|
+
name: string;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export type CreateQuotation = {
|
|
88
|
+
name: string;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export type updateProduct = {
|
|
92
|
+
product_remote_id: number;
|
|
93
|
+
division: number;
|
|
94
|
+
quantity: number
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export type LoadBasketParams = {
|
|
98
|
+
id: number;
|
|
99
|
+
};
|
|
100
|
+
|
|
56
101
|
export interface GetResponse<T> {
|
|
57
102
|
count: number;
|
|
58
103
|
next: null;
|
|
59
104
|
previous: null;
|
|
60
105
|
results: T[];
|
|
61
106
|
}
|
|
107
|
+
|
|
108
|
+
export type DraftResponse = {
|
|
109
|
+
id: number;
|
|
110
|
+
name: string;
|
|
111
|
+
total_amount: number;
|
|
112
|
+
total_quantity: number;
|
|
113
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { InputHTMLAttributes, HTMLAttributes } from 'react';
|
|
2
|
+
|
|
3
|
+
export type Validator = {
|
|
4
|
+
regex: {
|
|
5
|
+
regex: string;
|
|
6
|
+
message: string;
|
|
7
|
+
inverse_match: boolean;
|
|
8
|
+
};
|
|
9
|
+
max_length: number;
|
|
10
|
+
required: boolean;
|
|
11
|
+
min_length: number;
|
|
12
|
+
max_value: number;
|
|
13
|
+
min_value: number;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type FormField = {
|
|
17
|
+
chosen: boolean;
|
|
18
|
+
input_type: string;
|
|
19
|
+
id: string;
|
|
20
|
+
key: string;
|
|
21
|
+
validators: Validator;
|
|
22
|
+
label: string;
|
|
23
|
+
order: number;
|
|
24
|
+
class?: string;
|
|
25
|
+
attributes?: object | null;
|
|
26
|
+
labelClass?: string;
|
|
27
|
+
wrapperClass?: string;
|
|
28
|
+
placeholder?: string;
|
|
29
|
+
choices?: string[];
|
|
30
|
+
[key: string]: any;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type Schema = FormField[];
|
|
34
|
+
|
|
35
|
+
export type FormType = {
|
|
36
|
+
pk: number;
|
|
37
|
+
schema: Schema;
|
|
38
|
+
template: string;
|
|
39
|
+
is_active: boolean;
|
|
40
|
+
url: string;
|
|
41
|
+
name: string;
|
|
42
|
+
pretty_url: any;
|
|
43
|
+
created_date: string;
|
|
44
|
+
modified_date: string;
|
|
45
|
+
formprettyurl_set: any[];
|
|
46
|
+
translations: null | any;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type FieldPropertiesType = {
|
|
50
|
+
key?: string;
|
|
51
|
+
className?: string | Record<string, boolean>;
|
|
52
|
+
attributes?: InputHTMLAttributes<object> | HTMLAttributes<object>;
|
|
53
|
+
labelClassName?: string;
|
|
54
|
+
wrapperClassName?: string;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export type AllFieldClassesType = {
|
|
58
|
+
className?: string | undefined;
|
|
59
|
+
labelClassName?: string | undefined;
|
|
60
|
+
wrapperClassName?: string | undefined;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export type FormPropertiesType = {
|
|
64
|
+
actionUrl: string;
|
|
65
|
+
className?: string;
|
|
66
|
+
};
|
package/types/commerce/index.ts
CHANGED
package/types/index.ts
CHANGED
|
@@ -222,7 +222,15 @@ export interface RootLayoutProps<
|
|
|
222
222
|
};
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
+
export type RadioProps = React.HTMLProps<HTMLInputElement>;
|
|
226
|
+
|
|
225
227
|
export interface AuthError {
|
|
226
228
|
type: string;
|
|
227
229
|
data?: any;
|
|
228
230
|
}
|
|
231
|
+
|
|
232
|
+
export interface IconProps extends React.ComponentPropsWithRef<'i'> {
|
|
233
|
+
name: string;
|
|
234
|
+
size?: number;
|
|
235
|
+
className?: string;
|
|
236
|
+
}
|
package/utils/index.ts
CHANGED
|
@@ -32,7 +32,7 @@ export function setCookie(name: string, val: string) {
|
|
|
32
32
|
export function removeCookie(name: string) {
|
|
33
33
|
const date = 'Thu, 01 Jan 1970 00:00:00 UTC';
|
|
34
34
|
|
|
35
|
-
document.cookie = name
|
|
35
|
+
document.cookie = `${name}=; expires=${date}; path=/;`;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/**
|
|
@@ -137,3 +137,14 @@ export const urlLocaleMatcherRegex = new RegExp(
|
|
|
137
137
|
.map((l) => l.value)
|
|
138
138
|
.join('|')})`
|
|
139
139
|
);
|
|
140
|
+
|
|
141
|
+
export const getPosError = () => {
|
|
142
|
+
const error = JSON.parse(getCookie('pz-pos-error') ?? '{}');
|
|
143
|
+
|
|
144
|
+
// delete 'pz-pos-error' cookie when refreshing or closing page
|
|
145
|
+
window.addEventListener('beforeunload', () => {
|
|
146
|
+
removeCookie('pz-pos-error');
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
return error;
|
|
150
|
+
};
|