@akinon/pz-b2b 1.19.4 → 1.20.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/.prettierrc +13 -0
- package/CHANGELOG.md +6 -0
- package/package.json +1 -1
- package/src/basket-b2b/index.tsx +139 -31
- package/src/my-quotations/index.tsx +135 -37
- package/src/views/basket-b2b/basket-item.tsx +60 -36
- package/src/views/basket-b2b/remove-product-modal.tsx +27 -18
- package/src/views/basket-b2b/request-quote-modal.tsx +18 -13
- package/src/views/basket-b2b/save-cart-modal.tsx +11 -7
- package/src/views/my-quotations/tabs.tsx +31 -51
- package/src/views/product/store-modal.tsx +37 -14
package/.prettierrc
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"bracketSameLine": false,
|
|
3
|
+
"tabWidth": 2,
|
|
4
|
+
"singleQuote": true,
|
|
5
|
+
"jsxSingleQuote": false,
|
|
6
|
+
"bracketSpacing": true,
|
|
7
|
+
"semi": true,
|
|
8
|
+
"useTabs": false,
|
|
9
|
+
"arrowParens": "always",
|
|
10
|
+
"endOfLine": "lf",
|
|
11
|
+
"proseWrap": "never",
|
|
12
|
+
"trailingComma": "none"
|
|
13
|
+
}
|
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
package/src/basket-b2b/index.tsx
CHANGED
|
@@ -1,31 +1,72 @@
|
|
|
1
|
-
|
|
1
|
+
'use client';
|
|
2
2
|
|
|
3
|
-
import { useEffect, useState } from
|
|
4
|
-
import { pushCartView } from
|
|
3
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
4
|
+
import { pushCartView } from '../utils/gtm';
|
|
5
5
|
import {
|
|
6
6
|
LoaderSpinner,
|
|
7
7
|
Trans,
|
|
8
8
|
Price,
|
|
9
9
|
Icon,
|
|
10
|
-
Link
|
|
11
|
-
} from
|
|
12
|
-
import { useLocalization } from "@akinon/next/hooks";
|
|
10
|
+
Link
|
|
11
|
+
} from '@akinon/next/components';
|
|
13
12
|
import {
|
|
14
13
|
useGetBasketB2bQuery,
|
|
15
14
|
useGetDraftsQuery,
|
|
16
|
-
useLoadBasketMutation
|
|
17
|
-
} from
|
|
15
|
+
useLoadBasketMutation
|
|
16
|
+
} from '@akinon/next/data/client/b2b';
|
|
18
17
|
import {
|
|
19
18
|
BasketItem,
|
|
20
19
|
RequestQuoteModal,
|
|
21
20
|
RemoveProductModal,
|
|
22
|
-
SaveCartModal
|
|
23
|
-
} from
|
|
21
|
+
SaveCartModal
|
|
22
|
+
} from '../views/basket-b2b';
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
interface BasketB2bTranslationsProps {
|
|
25
|
+
my_cart: string;
|
|
26
|
+
back_to_shopping: string;
|
|
27
|
+
save_cart: string;
|
|
28
|
+
request_quote: string;
|
|
29
|
+
total_price: string;
|
|
30
|
+
save_cart_modal: {
|
|
31
|
+
title: string;
|
|
32
|
+
cart_name: string;
|
|
33
|
+
save_cart_button: string;
|
|
34
|
+
};
|
|
35
|
+
request_quote_modal: {
|
|
36
|
+
title: string;
|
|
37
|
+
quotation_name: string;
|
|
38
|
+
button: string;
|
|
39
|
+
};
|
|
40
|
+
remove_product_modal: {
|
|
41
|
+
title: string;
|
|
42
|
+
description: string;
|
|
43
|
+
add_to_favorites_button: string;
|
|
44
|
+
delete_product_button: string;
|
|
45
|
+
};
|
|
46
|
+
empty: {
|
|
47
|
+
title: string;
|
|
48
|
+
button: string;
|
|
49
|
+
choose_cart_placeholder: string;
|
|
50
|
+
};
|
|
51
|
+
table: {
|
|
52
|
+
basket_qty: string;
|
|
53
|
+
delete: string;
|
|
54
|
+
name_sku: string;
|
|
55
|
+
price: string;
|
|
56
|
+
quantity: string;
|
|
57
|
+
store_name: string;
|
|
58
|
+
store_qty: string;
|
|
59
|
+
subtotal: string;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
interface BasketB2bProps {
|
|
64
|
+
translations: Record<string, BasketB2bTranslationsProps>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function BasketB2b({ translations }: BasketB2bProps) {
|
|
26
68
|
const { data: basket, isLoading, isSuccess } = useGetBasketB2bQuery();
|
|
27
69
|
const [loadBasket] = useLoadBasketMutation();
|
|
28
|
-
const { t } = useLocalization();
|
|
29
70
|
const { data: dataDraft } = useGetDraftsQuery();
|
|
30
71
|
const [open, setOpen] = useState(false);
|
|
31
72
|
const [openRequestQuote, setRequestQuote] = useState(false);
|
|
@@ -33,6 +74,52 @@ export function BasketB2b() {
|
|
|
33
74
|
const [productToBeDeleted, setProductToBeDeleted] = useState(null);
|
|
34
75
|
const [selectedDraft, setSelectedDraft] = useState(null);
|
|
35
76
|
|
|
77
|
+
const defaultTranslations = {
|
|
78
|
+
my_cart: 'My Cart',
|
|
79
|
+
back_to_shopping: 'BACK TO SHOPPING',
|
|
80
|
+
save_cart: 'SAVE CART',
|
|
81
|
+
request_quote: 'REQUEST A QUOTE',
|
|
82
|
+
total_price: 'Catalog Total Price (Excl. Tax)',
|
|
83
|
+
save_cart_modal: {
|
|
84
|
+
title: 'SAVE CART',
|
|
85
|
+
cart_name: 'Cart Name',
|
|
86
|
+
save_cart_button: 'SAVE CART'
|
|
87
|
+
},
|
|
88
|
+
request_quote_modal: {
|
|
89
|
+
title: 'REQUEST A QUOTE',
|
|
90
|
+
quotation_name: 'Quotation Name',
|
|
91
|
+
button: 'SAVE'
|
|
92
|
+
},
|
|
93
|
+
remove_product_modal: {
|
|
94
|
+
title: 'DELETE AND ADD TO FAVORITES',
|
|
95
|
+
description:
|
|
96
|
+
'Would you like to add this product you want to delete to your favorites?',
|
|
97
|
+
add_to_favorites_button: 'ADD TO FAVORITES',
|
|
98
|
+
delete_product_button: 'delete product'
|
|
99
|
+
},
|
|
100
|
+
empty: {
|
|
101
|
+
title:
|
|
102
|
+
'Your cart is empty, would you like to continue with your saved carts?',
|
|
103
|
+
button: 'SELECT CART',
|
|
104
|
+
choose_cart_placeholder: 'Chose a cart'
|
|
105
|
+
},
|
|
106
|
+
table: {
|
|
107
|
+
basket_qty: '<Qty /> Products',
|
|
108
|
+
delete: 'Delete',
|
|
109
|
+
name_sku: 'NAME & SKU',
|
|
110
|
+
price: 'PRICE',
|
|
111
|
+
quantity: 'QTY',
|
|
112
|
+
store_name: 'STORE NAME',
|
|
113
|
+
store_qty: 'STORE QTY',
|
|
114
|
+
subtotal: 'SUBTOTAL'
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const _translations = useMemo(
|
|
119
|
+
() => ({ ...defaultTranslations, ...translations }),
|
|
120
|
+
[translations]
|
|
121
|
+
);
|
|
122
|
+
|
|
36
123
|
const handleSubmit = (e) => {
|
|
37
124
|
e.preventDefault();
|
|
38
125
|
|
|
@@ -46,7 +133,7 @@ export function BasketB2b() {
|
|
|
46
133
|
useEffect(() => {
|
|
47
134
|
if (isSuccess) {
|
|
48
135
|
const products = basket?.basket_items.map((basketItem) => ({
|
|
49
|
-
...basketItem.product
|
|
136
|
+
...basketItem.product
|
|
50
137
|
}));
|
|
51
138
|
pushCartView(products);
|
|
52
139
|
}
|
|
@@ -67,19 +154,19 @@ export function BasketB2b() {
|
|
|
67
154
|
<div className="w-full">
|
|
68
155
|
<div className="flex items-center justify-between py-2 lg:py-3">
|
|
69
156
|
<h2 className="text-xl lg:text-2xl font-light">
|
|
70
|
-
{
|
|
157
|
+
{_translations.my_cart}
|
|
71
158
|
</h2>
|
|
72
159
|
|
|
73
160
|
<Link href="/" className="text-xs hover:text-secondary-500">
|
|
74
|
-
{
|
|
161
|
+
{_translations.back_to_shopping}
|
|
75
162
|
</Link>
|
|
76
163
|
</div>
|
|
77
164
|
|
|
78
165
|
<div className="px-5 py-4 border border-b-0 border-gray-200 text-xs text-[#363636]">
|
|
79
166
|
<Trans
|
|
80
|
-
i18nKey=
|
|
167
|
+
i18nKey={_translations.table.basket_qty}
|
|
81
168
|
components={{
|
|
82
|
-
Qty: <span>{basket.total_quantity}</span
|
|
169
|
+
Qty: <span>{basket.total_quantity}</span>
|
|
83
170
|
}}
|
|
84
171
|
/>
|
|
85
172
|
</div>
|
|
@@ -99,20 +186,20 @@ export function BasketB2b() {
|
|
|
99
186
|
</div>
|
|
100
187
|
</th>
|
|
101
188
|
<th className="text-xs text-[#99999d] font-normal border border-[#d5dadb] py-3">
|
|
102
|
-
{
|
|
189
|
+
{_translations.table.name_sku}
|
|
103
190
|
</th>
|
|
104
191
|
<th className="text-xs text-[#99999d] font-normal border border-[#d5dadb] py-3">
|
|
105
|
-
{
|
|
192
|
+
{_translations.table.price}
|
|
106
193
|
</th>
|
|
107
194
|
<th className="text-xs text-[#99999d] font-normal border border-[#d5dadb] py-3">
|
|
108
|
-
{
|
|
195
|
+
{_translations.table.quantity}
|
|
109
196
|
</th>
|
|
110
197
|
<th className="text-xs text-[#99999d] font-normal border border-[#d5dadb] py-3">
|
|
111
|
-
{
|
|
198
|
+
{_translations.table.store_name}
|
|
112
199
|
</th>
|
|
113
200
|
<th className="text-xs text-[#99999d] font-normal border border-[#d5dadb] py-3">
|
|
114
201
|
<div className="flex items-center justify-between pl-5 pr-2.5">
|
|
115
|
-
<span>{
|
|
202
|
+
<span>{_translations.table.store_qty}</span>
|
|
116
203
|
<Icon
|
|
117
204
|
name="b2b-distribute"
|
|
118
205
|
size={18}
|
|
@@ -121,7 +208,7 @@ export function BasketB2b() {
|
|
|
121
208
|
</div>
|
|
122
209
|
</th>
|
|
123
210
|
<th className="text-xs text-[#99999d] font-normal border border-[#d5dadb] py-3">
|
|
124
|
-
{
|
|
211
|
+
{_translations.table.subtotal}
|
|
125
212
|
</th>
|
|
126
213
|
</tr>
|
|
127
214
|
</thead>
|
|
@@ -133,6 +220,7 @@ export function BasketB2b() {
|
|
|
133
220
|
key={index}
|
|
134
221
|
setRemoveProduct={setRemoveProduct}
|
|
135
222
|
setProductToBeDeleted={setProductToBeDeleted}
|
|
223
|
+
deleteText={_translations.table.delete}
|
|
136
224
|
/>
|
|
137
225
|
))}
|
|
138
226
|
</tbody>
|
|
@@ -143,7 +231,7 @@ export function BasketB2b() {
|
|
|
143
231
|
<div className="flex justify-end">
|
|
144
232
|
<div className="w-full md:w-[426px]">
|
|
145
233
|
<div className="w-full flex justify-between items-center text-sm text-[#3f4b5c] border border-[#d5dadb] py-6 px-4 mt-7 md:mt-0 md:px-5">
|
|
146
|
-
<span>{
|
|
234
|
+
<span>{_translations.total_price}</span>
|
|
147
235
|
<Price value={basket.total_amount} />
|
|
148
236
|
</div>
|
|
149
237
|
|
|
@@ -151,14 +239,14 @@ export function BasketB2b() {
|
|
|
151
239
|
onClick={() => setRequestQuote(true)}
|
|
152
240
|
className="mt-4 lg:mt-5 w-full h-10 text-white bg-black border border-black text-sm hover:bg-white hover:text-black transition-colors"
|
|
153
241
|
>
|
|
154
|
-
{
|
|
242
|
+
{_translations.request_quote}
|
|
155
243
|
</button>
|
|
156
244
|
|
|
157
245
|
<button
|
|
158
246
|
onClick={() => setOpen(true)}
|
|
159
247
|
className="mt-4 lg:mt-2.5 w-full h-10 bg-white text-[#2189ff] border border-[#2189ff] text-sm hover:bg-[#2189ff] hover:text-white transition-colors"
|
|
160
248
|
>
|
|
161
|
-
{
|
|
249
|
+
{_translations.empty.title}
|
|
162
250
|
</button>
|
|
163
251
|
</div>
|
|
164
252
|
</div>
|
|
@@ -175,7 +263,7 @@ export function BasketB2b() {
|
|
|
175
263
|
0
|
|
176
264
|
</span>
|
|
177
265
|
</div>
|
|
178
|
-
<p className="text-xs">{
|
|
266
|
+
<p className="text-xs">{_translations.empty.title}</p>
|
|
179
267
|
</div>
|
|
180
268
|
|
|
181
269
|
<form onSubmit={(e) => handleSubmit(e)}>
|
|
@@ -186,7 +274,7 @@ export function BasketB2b() {
|
|
|
186
274
|
className="w-[230px] h-[40px] mb-1 border border-[#d4d4d4] px-4 appearance-none text-sm outline-none cursor-pointer"
|
|
187
275
|
>
|
|
188
276
|
<option value="">
|
|
189
|
-
{
|
|
277
|
+
{_translations.empty.choose_cart_placeholder}
|
|
190
278
|
</option>
|
|
191
279
|
{dataDraft &&
|
|
192
280
|
dataDraft.map((draft) => (
|
|
@@ -201,18 +289,38 @@ export function BasketB2b() {
|
|
|
201
289
|
type="submit"
|
|
202
290
|
className="px-10 mt-2 w-full md:w-[230px] h-[40px] text-sm font-medium bg-black text-white cursor-pointer"
|
|
203
291
|
>
|
|
204
|
-
{
|
|
292
|
+
{_translations.empty.button}
|
|
205
293
|
</button>
|
|
206
294
|
</form>
|
|
207
295
|
</div>
|
|
208
296
|
))}
|
|
209
297
|
|
|
210
|
-
<SaveCartModal
|
|
211
|
-
|
|
298
|
+
<SaveCartModal
|
|
299
|
+
setOpen={setOpen}
|
|
300
|
+
open={open}
|
|
301
|
+
modalTitle={_translations.save_cart_modal.title}
|
|
302
|
+
modalCartName={_translations.save_cart_modal.cart_name}
|
|
303
|
+
saveCartButton={_translations.save_cart_modal.save_cart_button}
|
|
304
|
+
/>
|
|
305
|
+
<RequestQuoteModal
|
|
306
|
+
setOpen={setRequestQuote}
|
|
307
|
+
open={openRequestQuote}
|
|
308
|
+
modalTitle={_translations.request_quote_modal.title}
|
|
309
|
+
modalQuotationName={_translations.request_quote_modal.quotation_name}
|
|
310
|
+
modalButton={_translations.request_quote_modal.button}
|
|
311
|
+
/>
|
|
212
312
|
<RemoveProductModal
|
|
213
313
|
setOpen={setRemoveProduct}
|
|
214
314
|
open={openRemoveProduct}
|
|
215
315
|
basketItem={productToBeDeleted}
|
|
316
|
+
modalTitle={_translations.remove_product_modal.title}
|
|
317
|
+
modalDescription={_translations.remove_product_modal.description}
|
|
318
|
+
addToFavoritesButton={
|
|
319
|
+
_translations.remove_product_modal.add_to_favorites_button
|
|
320
|
+
}
|
|
321
|
+
deleteProductButton={
|
|
322
|
+
_translations.remove_product_modal.delete_product_button
|
|
323
|
+
}
|
|
216
324
|
/>
|
|
217
325
|
</div>
|
|
218
326
|
</>
|
|
@@ -1,45 +1,126 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
GetQuotationsResponse,
|
|
5
|
+
useGetQuotationsQuery
|
|
6
|
+
} from '@akinon/next/data/client/account';
|
|
5
7
|
import { LoaderSpinner, Link, Pagination } from '@akinon/next/components';
|
|
6
8
|
import { Tabs } from '../views/my-quotations/tabs';
|
|
7
9
|
import clsx from 'clsx';
|
|
8
|
-
import React, { useEffect, useState } from 'react';
|
|
10
|
+
import React, { useEffect, useMemo, useState } from 'react';
|
|
9
11
|
import { twMerge } from 'tailwind-merge';
|
|
10
|
-
import { useSearchParams } from 'next/navigation';
|
|
12
|
+
import { useSearchParams, usePathname } from 'next/navigation';
|
|
13
|
+
import { useRouter } from '@akinon/next/hooks';
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
interface AccountMyQuotationsTranslationsProps {
|
|
16
|
+
tableName: string;
|
|
17
|
+
tableDate: string;
|
|
18
|
+
tableStatus: string;
|
|
19
|
+
tableNumber: string;
|
|
20
|
+
tableActions: string;
|
|
21
|
+
tabAllQuotas: string;
|
|
22
|
+
tabPendingQuotas: string;
|
|
23
|
+
tabRejectedQuotas: string;
|
|
24
|
+
tabApprovedQuotas: string;
|
|
25
|
+
tabRequiresMyApproval: string;
|
|
26
|
+
tabTitleAllQuotas: string;
|
|
27
|
+
tabTitlePendingQuotas: string;
|
|
28
|
+
tabTitleRejectedQuotas: string;
|
|
29
|
+
tabTitleApprovedQuotas: string;
|
|
30
|
+
tabTitleRequiresMyApproval: string;
|
|
31
|
+
}
|
|
32
|
+
interface AccountMyQuotationsProps {
|
|
33
|
+
translations: Record<string, AccountMyQuotationsTranslationsProps>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function AccountMyQuotations({
|
|
37
|
+
translations
|
|
38
|
+
}: AccountMyQuotationsProps) {
|
|
39
|
+
const searchParams = new URLSearchParams(useSearchParams());
|
|
40
|
+
const router = useRouter();
|
|
41
|
+
const pathname = usePathname();
|
|
42
|
+
const { data: quotations, isLoading } = useGetQuotationsQuery({
|
|
43
|
+
page: Number(searchParams.get('page') || 1)
|
|
44
|
+
});
|
|
45
|
+
const { data: quotationsPending } = useGetQuotationsQuery({
|
|
46
|
+
page: Number(searchParams.get('page') || 1),
|
|
47
|
+
status: 'pending'
|
|
48
|
+
});
|
|
49
|
+
const { data: quotationsRejected } = useGetQuotationsQuery({
|
|
50
|
+
page: Number(searchParams.get('page') || 1),
|
|
51
|
+
status: 'rejected'
|
|
52
|
+
});
|
|
53
|
+
const { data: quotationsApproved } = useGetQuotationsQuery({
|
|
54
|
+
page: Number(searchParams.get('page') || 1),
|
|
55
|
+
status: 'approved'
|
|
56
|
+
});
|
|
57
|
+
const { data: quotationsSendingApprovalRequest } = useGetQuotationsQuery({
|
|
58
|
+
page: Number(searchParams.get('page') || 1),
|
|
59
|
+
status: 'sending_approval_request'
|
|
60
|
+
});
|
|
15
61
|
const [activeTab, setActiveTab] = useState('all');
|
|
16
|
-
const [
|
|
17
|
-
|
|
62
|
+
const [selectedQuotations, setSelectedQuotations] =
|
|
63
|
+
useState<GetQuotationsResponse>();
|
|
64
|
+
|
|
65
|
+
const defaultTranslations = {
|
|
66
|
+
tableName: 'Name',
|
|
67
|
+
tableDate: 'Date',
|
|
68
|
+
tableStatus: 'Status',
|
|
69
|
+
tableNumber: 'Number',
|
|
70
|
+
tableActions: 'Actions',
|
|
71
|
+
tabAllQuotas: 'All Quotas',
|
|
72
|
+
tabPendingQuotas: 'Pending',
|
|
73
|
+
tabRejectedQuotas: 'Rejected',
|
|
74
|
+
tabApprovedQuotas: 'Approved',
|
|
75
|
+
tabRequiresMyApproval: 'Requires My Approval',
|
|
76
|
+
tabTitleAllQuotas: 'All Quotas',
|
|
77
|
+
tabTitlePendingQuotas: 'Pending Quotas',
|
|
78
|
+
tabTitleRejectedQuotas: 'Rejected Quotas',
|
|
79
|
+
tabTitleApprovedQuotas: 'Approved Quotas',
|
|
80
|
+
tabTitleRequiresMyApproval: 'Requires My Approval'
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const _translations = useMemo(
|
|
84
|
+
() => ({ ...defaultTranslations, ...translations }),
|
|
85
|
+
[translations]
|
|
18
86
|
);
|
|
19
87
|
|
|
20
88
|
const thClasses =
|
|
21
89
|
'px-6 py-3 bg-[#f1f3f9] text-xs text-[#99999d] border-r border-[#c8daec] font-normal text-left';
|
|
22
|
-
const thTitles = [
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return quotation.status === activeStatus;
|
|
30
|
-
};
|
|
90
|
+
const thTitles = [
|
|
91
|
+
_translations.tableName,
|
|
92
|
+
_translations.tableDate,
|
|
93
|
+
_translations.tableStatus,
|
|
94
|
+
_translations.tableNumber,
|
|
95
|
+
_translations.tableActions
|
|
96
|
+
];
|
|
31
97
|
|
|
32
98
|
useEffect(() => {
|
|
33
|
-
if (
|
|
34
|
-
|
|
99
|
+
if (activeTab === 'all') {
|
|
100
|
+
setSelectedQuotations(quotations);
|
|
101
|
+
} else if (activeTab === 'pending') {
|
|
102
|
+
setSelectedQuotations(quotationsPending);
|
|
103
|
+
} else if (activeTab === 'rejected') {
|
|
104
|
+
setSelectedQuotations(quotationsRejected);
|
|
105
|
+
} else if (activeTab === 'approved') {
|
|
106
|
+
setSelectedQuotations(quotationsApproved);
|
|
107
|
+
} else if (activeTab === 'sending_approval_request') {
|
|
108
|
+
setSelectedQuotations(quotationsSendingApprovalRequest);
|
|
35
109
|
}
|
|
110
|
+
}, [
|
|
111
|
+
selectedQuotations,
|
|
112
|
+
activeTab,
|
|
113
|
+
quotations,
|
|
114
|
+
quotationsPending,
|
|
115
|
+
quotationsRejected,
|
|
116
|
+
quotationsApproved,
|
|
117
|
+
quotationsSendingApprovalRequest
|
|
118
|
+
]);
|
|
36
119
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
setFilteredQuotations(_filteredQuotations);
|
|
42
|
-
}, [quotations, activeTab]);
|
|
120
|
+
useEffect(() => {
|
|
121
|
+
searchParams.delete('page');
|
|
122
|
+
router.push(pathname + '?' + searchParams.toString());
|
|
123
|
+
}, [activeTab]);
|
|
43
124
|
|
|
44
125
|
if (isLoading) {
|
|
45
126
|
return <LoaderSpinner />;
|
|
@@ -50,19 +131,36 @@ export function AccountMyQuotations() {
|
|
|
50
131
|
<Tabs
|
|
51
132
|
setActiveTab={setActiveTab}
|
|
52
133
|
activeTab={activeTab}
|
|
53
|
-
|
|
134
|
+
quotationsCount={quotations?.count}
|
|
135
|
+
pendingCount={quotationsPending?.count}
|
|
136
|
+
rejectedCount={quotationsRejected?.count}
|
|
137
|
+
approvedCount={quotationsApproved?.count}
|
|
138
|
+
sendingApprovalRequestCount={quotationsSendingApprovalRequest?.count}
|
|
139
|
+
tabAllQuotas={_translations.tabAllQuotas}
|
|
140
|
+
tabPendingQuotas={_translations.tabPendingQuotas}
|
|
141
|
+
tabRejectedQuotas={_translations.tabRejectedQuotas}
|
|
142
|
+
tabApprovedQuotas={_translations.tabApprovedQuotas}
|
|
143
|
+
tabRequiresMyApproval={_translations.tabRequiresMyApproval}
|
|
54
144
|
/>
|
|
55
145
|
|
|
56
146
|
<div className="border border-[#d5dadb] mb-10 overflow-x-auto lg:overflow-hidden">
|
|
57
147
|
<p className="px-6 py-5 text-[#363636]">
|
|
58
|
-
<span hidden={activeTab !== 'all'}>
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
<span hidden={activeTab !== '
|
|
148
|
+
<span hidden={activeTab !== 'all'}>
|
|
149
|
+
{_translations.tabTitleAllQuotas}
|
|
150
|
+
</span>
|
|
151
|
+
<span hidden={activeTab !== 'pending'}>
|
|
152
|
+
{_translations.tabTitlePendingQuotas}
|
|
153
|
+
</span>
|
|
154
|
+
<span hidden={activeTab !== 'rejected'}>
|
|
155
|
+
{_translations.tabTitleRejectedQuotas}
|
|
156
|
+
</span>
|
|
157
|
+
<span hidden={activeTab !== 'approved'}>
|
|
158
|
+
{_translations.tabTitleApprovedQuotas}
|
|
159
|
+
</span>
|
|
62
160
|
<span hidden={activeTab !== 'sending_approval_request'}>
|
|
63
|
-
|
|
161
|
+
{_translations.tabTitleRequiresMyApproval}
|
|
64
162
|
</span>
|
|
65
|
-
(<span>{
|
|
163
|
+
(<span>{selectedQuotations?.count}</span>)
|
|
66
164
|
</p>
|
|
67
165
|
|
|
68
166
|
<table className="w-full">
|
|
@@ -82,7 +180,7 @@ export function AccountMyQuotations() {
|
|
|
82
180
|
</tr>
|
|
83
181
|
</thead>
|
|
84
182
|
<tbody>
|
|
85
|
-
{
|
|
183
|
+
{selectedQuotations?.results?.map((quotation) => (
|
|
86
184
|
<tr
|
|
87
185
|
key={quotation.id}
|
|
88
186
|
className="h-[64px] lg:h-[75px] even:bg-[#fbfcfd]"
|
|
@@ -122,11 +220,11 @@ export function AccountMyQuotations() {
|
|
|
122
220
|
</table>
|
|
123
221
|
</div>
|
|
124
222
|
|
|
125
|
-
{
|
|
223
|
+
{selectedQuotations?.count > 0 && (
|
|
126
224
|
<Pagination
|
|
127
|
-
total={
|
|
225
|
+
total={selectedQuotations.count}
|
|
128
226
|
limit={20}
|
|
129
|
-
numberOfPages={Math.ceil(
|
|
227
|
+
numberOfPages={Math.ceil(selectedQuotations.count / 20)}
|
|
130
228
|
containerClassName="lg:justify-end"
|
|
131
229
|
currentPage={Number(searchParams.get('page')) || 1}
|
|
132
230
|
/>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { useLocalization } from '@akinon/next/hooks';
|
|
4
3
|
import { Image, Price, Input, Icon } from '@akinon/next/components';
|
|
5
4
|
import { useUpdateProductMutation } from '@akinon/next/data/client/b2b';
|
|
6
5
|
import { BasketItemType } from '@akinon/next/types';
|
|
@@ -10,11 +9,12 @@ interface Props {
|
|
|
10
9
|
basketItem: BasketItemType;
|
|
11
10
|
setProductToBeDeleted: (item: BasketItemType) => void;
|
|
12
11
|
setRemoveProduct: (open: boolean) => void;
|
|
12
|
+
deleteText: string;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export const BasketItem = (props: Props) => {
|
|
16
|
-
const {
|
|
17
|
-
|
|
16
|
+
const { basketItem, setRemoveProduct, setProductToBeDeleted, deleteText } =
|
|
17
|
+
props;
|
|
18
18
|
const [updateProduct] = useUpdateProductMutation();
|
|
19
19
|
const [divisionErrors, setDivisionErrors] = useState([]);
|
|
20
20
|
|
|
@@ -49,29 +49,41 @@ export const BasketItem = (props: Props) => {
|
|
|
49
49
|
<>
|
|
50
50
|
<tr>
|
|
51
51
|
<td>
|
|
52
|
-
<div className=
|
|
53
|
-
<div className=
|
|
54
|
-
<Icon name=
|
|
52
|
+
<div className="h-[183px] w-full flex flex-col justify-between items-center text-xs">
|
|
53
|
+
<div className="w-full flex items-center justify-center py-3 mb-3 border-b">
|
|
54
|
+
<Icon name="chevron-down" size={10} />
|
|
55
55
|
</div>
|
|
56
56
|
|
|
57
|
-
<div
|
|
58
|
-
|
|
57
|
+
<div
|
|
58
|
+
className="h-full overflow-hidden"
|
|
59
|
+
style={{
|
|
60
|
+
writingMode: 'vertical-rl',
|
|
61
|
+
textOrientation: 'mixed',
|
|
62
|
+
display: '-webkit-box',
|
|
63
|
+
lineClamp: 1,
|
|
64
|
+
WebkitLineClamp: 1,
|
|
65
|
+
WebkitBoxOrient: 'vertical'
|
|
66
|
+
}}
|
|
59
67
|
>
|
|
60
|
-
{basketItem.product.name}
|
|
68
|
+
{basketItem.product.name}
|
|
69
|
+
{basketItem.product.name}
|
|
70
|
+
{basketItem.product.name}
|
|
61
71
|
</div>
|
|
62
72
|
|
|
63
73
|
<button
|
|
64
|
-
onClick={() => {
|
|
65
|
-
|
|
74
|
+
onClick={() => {
|
|
75
|
+
setProductToBeDeleted(basketItem), setRemoveProduct(true);
|
|
76
|
+
}}
|
|
77
|
+
className="py-1.5 border-t w-full flex items-center justify-center"
|
|
66
78
|
style={{ writingMode: 'vertical-rl', textOrientation: 'mixed' }}
|
|
67
79
|
>
|
|
68
|
-
{
|
|
80
|
+
{deleteText}
|
|
69
81
|
</button>
|
|
70
82
|
</div>
|
|
71
83
|
</td>
|
|
72
84
|
|
|
73
|
-
<td className=
|
|
74
|
-
<div className=
|
|
85
|
+
<td className="border text-center">
|
|
86
|
+
<div className="min-w-[80px] w-full h-full flex items-center justify-center">
|
|
75
87
|
<Image
|
|
76
88
|
src={basketItem.product.product_image}
|
|
77
89
|
alt={basketItem.product.name}
|
|
@@ -81,30 +93,39 @@ export const BasketItem = (props: Props) => {
|
|
|
81
93
|
</div>
|
|
82
94
|
</td>
|
|
83
95
|
|
|
84
|
-
<td className=
|
|
85
|
-
<div className=
|
|
86
|
-
<div className=
|
|
87
|
-
<div className=
|
|
96
|
+
<td className="min-w-[181px] text-sm border text-[#3f4b5c] leading-normal">
|
|
97
|
+
<div className="px-4">
|
|
98
|
+
<div className="break-words">{basketItem.product.name}</div>
|
|
99
|
+
<div className="break-words">SKU: {basketItem.product.sku}</div>
|
|
88
100
|
</div>
|
|
89
101
|
</td>
|
|
90
102
|
|
|
91
|
-
<td className=
|
|
92
|
-
<Price value={basketItem.price} className=
|
|
103
|
+
<td className="text-sm border text-[#3f4b5c] leading-normal text-right px-3">
|
|
104
|
+
<Price value={basketItem.price} className="w-max block" />
|
|
93
105
|
</td>
|
|
94
106
|
|
|
95
|
-
<td className=
|
|
96
|
-
<div className=
|
|
97
|
-
{basketItem.quantity}
|
|
98
|
-
</div>
|
|
107
|
+
<td className="text-sm border text-[#3f4b5c] leading-normal text-center">
|
|
108
|
+
<div className="min-w-[90px]">{basketItem.quantity}</div>
|
|
99
109
|
</td>
|
|
100
110
|
|
|
101
|
-
<td className=
|
|
102
|
-
<div className=
|
|
111
|
+
<td className="border">
|
|
112
|
+
<div className="min-w-[140px]">
|
|
103
113
|
{basketItem.divisions.map((division) => (
|
|
104
|
-
<div
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
114
|
+
<div
|
|
115
|
+
className="py-5 px-1 border-b last:border-0 text-center flex items-center justify-center gap-x-1"
|
|
116
|
+
key={division.name}
|
|
117
|
+
>
|
|
118
|
+
<Icon name="b2b-info" size={18} className="text-[#293245]" />
|
|
119
|
+
|
|
120
|
+
<div
|
|
121
|
+
className="text-sm underline text-[#3f4b5c] overflow-hidden"
|
|
122
|
+
style={{
|
|
123
|
+
display: '-webkit-box',
|
|
124
|
+
lineClamp: 1,
|
|
125
|
+
WebkitLineClamp: 1,
|
|
126
|
+
WebkitBoxOrient: 'vertical'
|
|
127
|
+
}}
|
|
128
|
+
>
|
|
108
129
|
{division.name}
|
|
109
130
|
</div>
|
|
110
131
|
</div>
|
|
@@ -112,12 +133,15 @@ export const BasketItem = (props: Props) => {
|
|
|
112
133
|
</div>
|
|
113
134
|
</td>
|
|
114
135
|
|
|
115
|
-
<td className=
|
|
116
|
-
<div className=
|
|
136
|
+
<td className="border">
|
|
137
|
+
<div className="min-w-[120px]">
|
|
117
138
|
{basketItem.divisions.map((division, index) => (
|
|
118
|
-
<div
|
|
139
|
+
<div
|
|
140
|
+
className="relative py-5 border-b last:border-0 text-center flex flex-col gap-y-1 items-center justify-center"
|
|
141
|
+
key={`division-${division.id}`}
|
|
142
|
+
>
|
|
119
143
|
<Input
|
|
120
|
-
className=
|
|
144
|
+
className="appearance-none outline-none w-20 h-6 px-2 py-0 border border-[#c8daec] rounded text-right"
|
|
121
145
|
type="number"
|
|
122
146
|
value={division.quantity}
|
|
123
147
|
onChange={(e) => handleChange(division.id, e, index)}
|
|
@@ -135,8 +159,8 @@ export const BasketItem = (props: Props) => {
|
|
|
135
159
|
</div>
|
|
136
160
|
</td>
|
|
137
161
|
|
|
138
|
-
<td className=
|
|
139
|
-
<Price value={basketItem.total_amount} className=
|
|
162
|
+
<td className="text-sm border text-[#3f4b5c] leading-normal text-right px-6">
|
|
163
|
+
<Price value={basketItem.total_amount} className="w-max block" />
|
|
140
164
|
</td>
|
|
141
165
|
</tr>
|
|
142
166
|
</>
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import { useDeleteProductMutation } from '@akinon/next/data/client/b2b';
|
|
4
4
|
import { Image } from '@akinon/next/components';
|
|
5
|
-
import { useLocalization } from '@akinon/next/hooks';
|
|
6
5
|
import React, { useEffect } from 'react';
|
|
7
6
|
import { BasketItemType } from '@akinon/next/types';
|
|
8
7
|
import { useAddFavoriteMutation } from '@akinon/next/data/client/wishlist';
|
|
@@ -11,19 +10,26 @@ import clsx from 'clsx';
|
|
|
11
10
|
export const RemoveProductModal = ({
|
|
12
11
|
open,
|
|
13
12
|
setOpen,
|
|
14
|
-
basketItem
|
|
13
|
+
basketItem,
|
|
14
|
+
modalTitle,
|
|
15
|
+
modalDescription,
|
|
16
|
+
addToFavoritesButton,
|
|
17
|
+
deleteProductButton
|
|
15
18
|
}: {
|
|
16
19
|
open: boolean;
|
|
17
20
|
setOpen: (open: boolean) => void;
|
|
18
|
-
basketItem: BasketItemType
|
|
21
|
+
basketItem: BasketItemType;
|
|
22
|
+
modalTitle: string;
|
|
23
|
+
modalDescription: string;
|
|
24
|
+
addToFavoritesButton: string;
|
|
25
|
+
deleteProductButton: string;
|
|
19
26
|
}) => {
|
|
20
|
-
const { t } = useLocalization();
|
|
21
27
|
const [addFavorite] = useAddFavoriteMutation();
|
|
22
28
|
const [deleteProduct, { isLoading, isSuccess }] = useDeleteProductMutation();
|
|
23
29
|
|
|
24
|
-
const handleClick = (addToFavorite:boolean) => {
|
|
25
|
-
if(addToFavorite) {
|
|
26
|
-
addFavorite(basketItem.product_remote_id)
|
|
30
|
+
const handleClick = (addToFavorite: boolean) => {
|
|
31
|
+
if (addToFavorite) {
|
|
32
|
+
addFavorite(basketItem.product_remote_id);
|
|
27
33
|
}
|
|
28
34
|
|
|
29
35
|
deleteProduct({ product_remote_id: basketItem.product_remote_id });
|
|
@@ -42,7 +48,7 @@ export const RemoveProductModal = ({
|
|
|
42
48
|
<div className="w-11/12 max-w-[344px] md:w-[370px] left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-50 fixed bg-white">
|
|
43
49
|
<header className="relative border-b border-[#d5dadb]">
|
|
44
50
|
<h3 className="text-left px-[30px] py-4 font-medium text-[17px]">
|
|
45
|
-
{
|
|
51
|
+
{modalTitle}
|
|
46
52
|
</h3>
|
|
47
53
|
|
|
48
54
|
<button
|
|
@@ -53,22 +59,25 @@ export const RemoveProductModal = ({
|
|
|
53
59
|
</button>
|
|
54
60
|
</header>
|
|
55
61
|
|
|
56
|
-
<p className="text-left px-[30px] py-4 text-sm">
|
|
57
|
-
{t('basket.b2b.remove_product_modal.description')}
|
|
58
|
-
</p>
|
|
62
|
+
<p className="text-left px-[30px] py-4 text-sm">{modalDescription}</p>
|
|
59
63
|
|
|
60
64
|
<div className="px-[30px] pb-5">
|
|
61
|
-
<div className=
|
|
65
|
+
<div className="flex items-center gap-x-3">
|
|
62
66
|
<div>
|
|
63
|
-
<Image
|
|
67
|
+
<Image
|
|
68
|
+
src={basketItem.product.product_image}
|
|
69
|
+
width={72}
|
|
70
|
+
height={93}
|
|
71
|
+
alt={basketItem.product.name}
|
|
72
|
+
/>
|
|
64
73
|
</div>
|
|
65
|
-
<div className=
|
|
74
|
+
<div className="text-[#3f4b5c] text-sm">
|
|
66
75
|
<p>{basketItem.product.name}</p>
|
|
67
76
|
<p>SKU: {basketItem.product.sku}</p>
|
|
68
77
|
</div>
|
|
69
78
|
</div>
|
|
70
79
|
|
|
71
|
-
<div className=
|
|
80
|
+
<div className="flex gap-y-3 flex-col items-center">
|
|
72
81
|
<button
|
|
73
82
|
type="submit"
|
|
74
83
|
className="h-8 bg-black text-white font-medium text-sm w-full"
|
|
@@ -80,7 +89,7 @@ export const RemoveProductModal = ({
|
|
|
80
89
|
<div className="w-4 h-4 border-2 border-gray-600 border-t-transparent rounded-full animate-spin" />
|
|
81
90
|
</div>
|
|
82
91
|
) : (
|
|
83
|
-
|
|
92
|
+
addToFavoritesButton
|
|
84
93
|
)}
|
|
85
94
|
</button>
|
|
86
95
|
|
|
@@ -98,7 +107,7 @@ export const RemoveProductModal = ({
|
|
|
98
107
|
<div className="w-4 h-4 border-2 border-gray-600 border-t-transparent rounded-full animate-spin" />
|
|
99
108
|
</div>
|
|
100
109
|
) : (
|
|
101
|
-
|
|
110
|
+
deleteProductButton
|
|
102
111
|
)}
|
|
103
112
|
</button>
|
|
104
113
|
</div>
|
|
@@ -106,4 +115,4 @@ export const RemoveProductModal = ({
|
|
|
106
115
|
</div>
|
|
107
116
|
</div>
|
|
108
117
|
);
|
|
109
|
-
}
|
|
118
|
+
};
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useCreateQuotationMutation } from '@akinon/next/data/client/b2b';
|
|
4
|
-
import { useLocalization } from '@akinon/next/hooks';
|
|
5
4
|
import React, { useEffect, useState } from 'react';
|
|
6
5
|
|
|
7
6
|
export const RequestQuoteModal = ({
|
|
8
7
|
open,
|
|
9
|
-
setOpen
|
|
8
|
+
setOpen,
|
|
9
|
+
modalTitle,
|
|
10
|
+
modalQuotationName,
|
|
11
|
+
modalButton
|
|
10
12
|
}: {
|
|
11
13
|
open: boolean;
|
|
12
14
|
setOpen: (open: boolean) => void;
|
|
15
|
+
modalTitle: string;
|
|
16
|
+
modalQuotationName: string;
|
|
17
|
+
modalButton: string;
|
|
13
18
|
}) => {
|
|
14
|
-
const {
|
|
15
|
-
|
|
19
|
+
const [createQuotation, { isLoading, isSuccess, error, isError }] =
|
|
20
|
+
useCreateQuotationMutation();
|
|
16
21
|
const [errors, setErrors] = useState([]);
|
|
17
22
|
const [cartName, setCartName] = useState<string>('');
|
|
18
23
|
|
|
@@ -23,11 +28,11 @@ export const RequestQuoteModal = ({
|
|
|
23
28
|
return;
|
|
24
29
|
}
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
createQuotation({ name: cartName }).then((res) => {
|
|
32
|
+
if ('error' in res && 'data' in res.error) {
|
|
33
|
+
setErrors((res.error.data as any).non_field_errors || []);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
31
36
|
};
|
|
32
37
|
|
|
33
38
|
useEffect(() => {
|
|
@@ -43,7 +48,7 @@ export const RequestQuoteModal = ({
|
|
|
43
48
|
<div className="w-11/12 max-w-[344px] md:w-[370px] left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-50 fixed bg-white">
|
|
44
49
|
<header className="relative border-b border-[#d5dadb]">
|
|
45
50
|
<h3 className="text-left px-[30px] py-4 font-medium text-lg">
|
|
46
|
-
{
|
|
51
|
+
{modalTitle}
|
|
47
52
|
</h3>
|
|
48
53
|
|
|
49
54
|
<button
|
|
@@ -57,7 +62,7 @@ export const RequestQuoteModal = ({
|
|
|
57
62
|
<div className="p-[30px]">
|
|
58
63
|
<form onSubmit={(e) => handleSubmit(e)}>
|
|
59
64
|
<label htmlFor="cart-name-input" className="text-xs block mb-2">
|
|
60
|
-
{
|
|
65
|
+
{modalQuotationName}
|
|
61
66
|
</label>
|
|
62
67
|
|
|
63
68
|
<input
|
|
@@ -86,7 +91,7 @@ export const RequestQuoteModal = ({
|
|
|
86
91
|
<div className="w-4 h-4 border-2 border-gray-600 border-t-transparent rounded-full animate-spin" />
|
|
87
92
|
</div>
|
|
88
93
|
) : (
|
|
89
|
-
|
|
94
|
+
modalButton
|
|
90
95
|
)}
|
|
91
96
|
</button>
|
|
92
97
|
</form>
|
|
@@ -94,4 +99,4 @@ export const RequestQuoteModal = ({
|
|
|
94
99
|
</div>
|
|
95
100
|
</div>
|
|
96
101
|
);
|
|
97
|
-
}
|
|
102
|
+
};
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useSaveBasketMutation } from '@akinon/next/data/client/b2b';
|
|
4
|
-
import { useLocalization } from '@akinon/next/hooks';
|
|
5
4
|
import React, { useEffect, useState } from 'react';
|
|
6
5
|
|
|
7
6
|
export const SaveCartModal = ({
|
|
8
7
|
open,
|
|
9
|
-
setOpen
|
|
8
|
+
setOpen,
|
|
9
|
+
modalTitle,
|
|
10
|
+
modalCartName,
|
|
11
|
+
saveCartButton
|
|
10
12
|
}: {
|
|
11
13
|
open: boolean;
|
|
12
14
|
setOpen: (open: boolean) => void;
|
|
15
|
+
modalTitle: string;
|
|
16
|
+
modalCartName: string;
|
|
17
|
+
saveCartButton: string;
|
|
13
18
|
}) => {
|
|
14
|
-
const { t } = useLocalization();
|
|
15
19
|
const [cartName, setCartName] = useState<string>('');
|
|
16
20
|
const [saveBasket, { isLoading, isSuccess }] = useSaveBasketMutation();
|
|
17
21
|
|
|
@@ -38,7 +42,7 @@ export const SaveCartModal = ({
|
|
|
38
42
|
<div className="w-11/12 max-w-[344px] md:w-[370px] left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-50 fixed bg-white">
|
|
39
43
|
<header className="relative border-b border-[#d5dadb]">
|
|
40
44
|
<h3 className="text-left px-[30px] py-4 font-medium text-lg">
|
|
41
|
-
{
|
|
45
|
+
{modalTitle}
|
|
42
46
|
</h3>
|
|
43
47
|
<button
|
|
44
48
|
className="absolute right-[30px] top-5"
|
|
@@ -50,7 +54,7 @@ export const SaveCartModal = ({
|
|
|
50
54
|
<div className="p-[30px]">
|
|
51
55
|
<form onSubmit={(e) => handleSubmit(e)}>
|
|
52
56
|
<label htmlFor="cart-name-input" className="text-xs block mb-2">
|
|
53
|
-
{
|
|
57
|
+
{modalCartName}
|
|
54
58
|
</label>
|
|
55
59
|
<input
|
|
56
60
|
id="cart-name-input"
|
|
@@ -69,7 +73,7 @@ export const SaveCartModal = ({
|
|
|
69
73
|
<div className="w-4 h-4 border-2 border-gray-600 border-t-transparent rounded-full animate-spin" />
|
|
70
74
|
</div>
|
|
71
75
|
) : (
|
|
72
|
-
|
|
76
|
+
saveCartButton
|
|
73
77
|
)}
|
|
74
78
|
</button>
|
|
75
79
|
</form>
|
|
@@ -77,4 +81,4 @@ export const SaveCartModal = ({
|
|
|
77
81
|
</div>
|
|
78
82
|
</div>
|
|
79
83
|
);
|
|
80
|
-
}
|
|
84
|
+
};
|
|
@@ -1,57 +1,38 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { GetQuotationsResponse } from '@akinon/next/data/client/account';
|
|
4
3
|
import clsx from 'clsx';
|
|
5
|
-
import React, { Dispatch, SetStateAction
|
|
4
|
+
import React, { Dispatch, SetStateAction } from 'react';
|
|
6
5
|
import { twMerge } from 'tailwind-merge';
|
|
7
6
|
|
|
8
|
-
type
|
|
9
|
-
|
|
7
|
+
type TabsPropsType = {
|
|
8
|
+
quotationsCount: number;
|
|
9
|
+
pendingCount: number;
|
|
10
|
+
rejectedCount: number;
|
|
11
|
+
approvedCount: number;
|
|
12
|
+
sendingApprovalRequestCount: number;
|
|
10
13
|
setActiveTab: Dispatch<SetStateAction<string>>;
|
|
11
14
|
activeTab: string;
|
|
15
|
+
tabAllQuotas: string;
|
|
16
|
+
tabPendingQuotas: string;
|
|
17
|
+
tabRejectedQuotas: string;
|
|
18
|
+
tabApprovedQuotas: string;
|
|
19
|
+
tabRequiresMyApproval: string;
|
|
12
20
|
};
|
|
13
21
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
setTabCounts(initialTabCountsState);
|
|
29
|
-
|
|
30
|
-
quotations?.forEach((quotation) => {
|
|
31
|
-
if (quotation.status === 'pending') {
|
|
32
|
-
setTabCounts((prevCount) => ({
|
|
33
|
-
...prevCount,
|
|
34
|
-
pendingCount: prevCount.pendingCount + 1
|
|
35
|
-
}));
|
|
36
|
-
} else if (quotation.status === 'rejected') {
|
|
37
|
-
setTabCounts((prevCount) => ({
|
|
38
|
-
...prevCount,
|
|
39
|
-
rejectedCount: prevCount.rejectedCount + 1
|
|
40
|
-
}));
|
|
41
|
-
} else if (quotation.status === 'approved') {
|
|
42
|
-
setTabCounts((prevCount) => ({
|
|
43
|
-
...prevCount,
|
|
44
|
-
approvedCount: prevCount.approvedCount + 1
|
|
45
|
-
}));
|
|
46
|
-
} else if (quotation.status === 'sending_approval_request') {
|
|
47
|
-
setTabCounts((prevCount) => ({
|
|
48
|
-
...prevCount,
|
|
49
|
-
sendingApprovalRequestCount: prevCount.sendingApprovalRequestCount + 1
|
|
50
|
-
}));
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
}, [quotations]);
|
|
54
|
-
|
|
22
|
+
export function Tabs({
|
|
23
|
+
quotationsCount,
|
|
24
|
+
pendingCount,
|
|
25
|
+
rejectedCount,
|
|
26
|
+
approvedCount,
|
|
27
|
+
sendingApprovalRequestCount,
|
|
28
|
+
setActiveTab,
|
|
29
|
+
activeTab,
|
|
30
|
+
tabAllQuotas,
|
|
31
|
+
tabPendingQuotas,
|
|
32
|
+
tabRejectedQuotas,
|
|
33
|
+
tabApprovedQuotas,
|
|
34
|
+
tabRequiresMyApproval
|
|
35
|
+
}: TabsPropsType) {
|
|
55
36
|
return (
|
|
56
37
|
<div className="mb-9 mt-4 lg:mt-0">
|
|
57
38
|
<div className="relative flex justify-start lg:justify-between space-x-3 lg:space-x-4">
|
|
@@ -75,7 +56,7 @@ export function Tabs(props: TabsType) {
|
|
|
75
56
|
activeTab === 'all' && 'font-bold'
|
|
76
57
|
)}
|
|
77
58
|
>
|
|
78
|
-
|
|
59
|
+
{tabAllQuotas} (<span>{quotationsCount}</span>)
|
|
79
60
|
</span>
|
|
80
61
|
</div>
|
|
81
62
|
|
|
@@ -99,7 +80,7 @@ export function Tabs(props: TabsType) {
|
|
|
99
80
|
activeTab === 'pending' && 'font-bold'
|
|
100
81
|
)}
|
|
101
82
|
>
|
|
102
|
-
|
|
83
|
+
{tabPendingQuotas} (<span>{pendingCount}</span>)
|
|
103
84
|
</span>
|
|
104
85
|
</div>
|
|
105
86
|
|
|
@@ -123,7 +104,7 @@ export function Tabs(props: TabsType) {
|
|
|
123
104
|
activeTab === 'rejected' && 'font-bold'
|
|
124
105
|
)}
|
|
125
106
|
>
|
|
126
|
-
|
|
107
|
+
{tabRejectedQuotas} (<span>{rejectedCount}</span>)
|
|
127
108
|
</span>
|
|
128
109
|
</div>
|
|
129
110
|
|
|
@@ -147,7 +128,7 @@ export function Tabs(props: TabsType) {
|
|
|
147
128
|
activeTab === 'approved' && 'font-bold'
|
|
148
129
|
)}
|
|
149
130
|
>
|
|
150
|
-
|
|
131
|
+
{tabApprovedQuotas} (<span>{approvedCount}</span>)
|
|
151
132
|
</span>
|
|
152
133
|
</div>
|
|
153
134
|
|
|
@@ -172,8 +153,7 @@ export function Tabs(props: TabsType) {
|
|
|
172
153
|
activeTab === 'sending_approval_request' && 'font-bold'
|
|
173
154
|
)}
|
|
174
155
|
>
|
|
175
|
-
|
|
176
|
-
<span>{tabCounts.sendingApprovalRequestCount}</span>)
|
|
156
|
+
{tabRequiresMyApproval} (<span>{sendingApprovalRequestCount}</span>)
|
|
177
157
|
</span>
|
|
178
158
|
</div>
|
|
179
159
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useAddToBasketMutation } from '@akinon/next/data/client/b2b';
|
|
4
|
-
import { useLocalization } from '@akinon/next/hooks';
|
|
5
4
|
import { Division } from '@akinon/next/types';
|
|
6
5
|
import React, { useMemo, useState } from 'react';
|
|
7
6
|
import { twMerge } from 'tailwind-merge';
|
|
@@ -13,27 +12,51 @@ interface GetResponse<T> {
|
|
|
13
12
|
results: T[];
|
|
14
13
|
}
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
interface StoreModalTranslationsProps {
|
|
16
|
+
modalTitle: string;
|
|
17
|
+
storeName: string;
|
|
18
|
+
quantity: string;
|
|
19
|
+
validQuantity: string;
|
|
20
|
+
addToBasket: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface StoreModalProps {
|
|
22
24
|
storeData: GetResponse<Division>;
|
|
23
25
|
productPk: number;
|
|
24
26
|
open: boolean;
|
|
25
27
|
setOpen: (open: boolean) => void;
|
|
26
|
-
|
|
28
|
+
translations?: Record<string, any>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function StoreModal({
|
|
32
|
+
storeData,
|
|
33
|
+
productPk,
|
|
34
|
+
open,
|
|
35
|
+
setOpen,
|
|
36
|
+
translations
|
|
37
|
+
}: StoreModalProps) {
|
|
27
38
|
const initialStateSelectStore = {
|
|
28
39
|
id: null,
|
|
29
40
|
quantity: '0'
|
|
30
41
|
};
|
|
31
42
|
|
|
32
|
-
const { t } = useLocalization();
|
|
33
43
|
const [addToBasket] = useAddToBasketMutation();
|
|
34
44
|
const [selectedStore, setSelectedStore] = useState(initialStateSelectStore);
|
|
35
45
|
const inputRefs = {};
|
|
36
46
|
|
|
47
|
+
const defaultTranslations: StoreModalTranslationsProps = {
|
|
48
|
+
modalTitle: 'Select Store',
|
|
49
|
+
storeName: 'Store Name',
|
|
50
|
+
quantity: 'Quantity',
|
|
51
|
+
validQuantity: 'Please enter a valid quantity',
|
|
52
|
+
addToBasket: 'Add to Basket'
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const mergedTranslations = useMemo(
|
|
56
|
+
() => ({ ...defaultTranslations, ...translations }),
|
|
57
|
+
[translations]
|
|
58
|
+
);
|
|
59
|
+
|
|
37
60
|
const handleRadioChange = (storeId) => {
|
|
38
61
|
setSelectedStore((prevSelectedStore) => ({
|
|
39
62
|
...prevSelectedStore,
|
|
@@ -92,7 +115,7 @@ export function StoreModal({
|
|
|
92
115
|
<div className="w-full h-screen inset-0 md:inset-auto md:w-[600px] md:h-min md:left-1/2 md:top-1/2 md:-translate-x-1/2 md:-translate-y-1/2 z-50 fixed bg-white">
|
|
93
116
|
<header className="relative md:border-b border-[#d5dadb]">
|
|
94
117
|
<h3 className="text-left px-4 md:text-center py-4 font-bold text-lg">
|
|
95
|
-
{
|
|
118
|
+
{mergedTranslations.modalTitle}
|
|
96
119
|
</h3>
|
|
97
120
|
<button
|
|
98
121
|
className="absolute right-[15px] md:right-[30px] top-5"
|
|
@@ -110,10 +133,10 @@ export function StoreModal({
|
|
|
110
133
|
<tr className="border border-[#d7d7d7] bg-[#fafafa]">
|
|
111
134
|
<th className="w-[43px] border-r border-[#d7d7d7]"></th>
|
|
112
135
|
<th className="w-[190px] md:w-auto text-[11px] text-[#99999d] py-[18px] px-4 border-r border-[#d7d7d7] text-left font-normal">
|
|
113
|
-
{
|
|
136
|
+
{mergedTranslations.storeName}
|
|
114
137
|
</th>
|
|
115
138
|
<th className="max-w-[111px] w-[111px] md:w-[180px] md:max-w-[180px] text-[11px] text-[#99999d] py-[18px] text-right pr-6 md:pr-[37px] font-normal">
|
|
116
|
-
{
|
|
139
|
+
{mergedTranslations.quantity}
|
|
117
140
|
</th>
|
|
118
141
|
</tr>
|
|
119
142
|
</thead>
|
|
@@ -150,7 +173,7 @@ export function StoreModal({
|
|
|
150
173
|
</table>
|
|
151
174
|
|
|
152
175
|
<div className="fixed bottom-0 left-0 w-full p-[15px] md:p-0 md:static mt-5 shadow-[0_0_4px_0_rgba(0,0,0,0.2)] md:shadow-none">
|
|
153
|
-
<p hidden>{
|
|
176
|
+
<p hidden>{mergedTranslations.validQuantity}</p>
|
|
154
177
|
|
|
155
178
|
<button
|
|
156
179
|
className={twMerge(
|
|
@@ -160,7 +183,7 @@ export function StoreModal({
|
|
|
160
183
|
onClick={handleAddToBasket}
|
|
161
184
|
disabled={isButtonDisabled}
|
|
162
185
|
>
|
|
163
|
-
{
|
|
186
|
+
{mergedTranslations.addToBasket}
|
|
164
187
|
</button>
|
|
165
188
|
</div>
|
|
166
189
|
</div>
|