@akinon/pz-b2b 1.19.3 → 1.20.0
Sign up to get free protection for your applications and to get access to all the features.
- package/.prettierrc +13 -0
- package/CHANGELOG.md +8 -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>
|