@akinon/pz-b2b 1.62.0 → 1.64.0
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +8 -0
- package/README.md +23 -5
- package/package.json +1 -1
- package/src/basket-b2b/index.tsx +77 -11
- package/src/views/basket-b2b/index.ts +1 -0
- package/src/views/basket-b2b/upload-modal.tsx +106 -0
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
@@ -102,6 +102,15 @@ Add the translation inside the basket object. Apply this for every language
|
|
102
102
|
"add_to_favorites_button": "ADD TO FAVORITES",
|
103
103
|
"delete_product_button": "delete product"
|
104
104
|
},
|
105
|
+
"upload_file_modal": {
|
106
|
+
"select_file": "Select File",
|
107
|
+
"upload": "Upload",
|
108
|
+
"no_file_selected": "No file selected",
|
109
|
+
"error": {
|
110
|
+
"upload_failed": "An error occurred while uploading the file",
|
111
|
+
"no_file_selected": "Please select a file before uploading."
|
112
|
+
}
|
113
|
+
},
|
105
114
|
"empty": {
|
106
115
|
"title": "Your cart is empty, would you like to continue with your saved carts?",
|
107
116
|
"button": "SELECT CART",
|
@@ -141,6 +150,15 @@ Add the translation inside the basket object. Apply this for every language
|
|
141
150
|
"add_to_favorites_button": "FAVORİLERE EKLE",
|
142
151
|
"delete_product_button": "ürünü sil"
|
143
152
|
},
|
153
|
+
"upload_file_modal": {
|
154
|
+
"select_file": "Dosya Seç",
|
155
|
+
"upload": "Yükle",
|
156
|
+
"no_file_selected": "Dosya seçilmedi",
|
157
|
+
"error": {
|
158
|
+
"upload_failed": "Dosya yüklenirken bir hata oluştu",
|
159
|
+
"no_file_selected": "Lütfen yüklemeden önce bir dosya seçin"
|
160
|
+
}
|
161
|
+
},
|
144
162
|
"empty": {
|
145
163
|
"title": "Sepetiniz boş, kaydettiğiniz sepetlerle devam etmek ister misiniz?",
|
146
164
|
"button": "SEPETİ SEÇ",
|
@@ -189,7 +207,7 @@ rewrites: [
|
|
189
207
|
Add plugin name
|
190
208
|
|
191
209
|
```javascript
|
192
|
-
module.exports = [
|
210
|
+
module.exports = ['...', 'pz-b2b'];
|
193
211
|
```
|
194
212
|
|
195
213
|
##### File Path: src/views/product/product-info.tsx
|
@@ -197,7 +215,7 @@ module.exports = ["...", "pz-b2b"];
|
|
197
215
|
Add imports
|
198
216
|
|
199
217
|
```javascript
|
200
|
-
import { useB2b, StoreModal } from
|
218
|
+
import { useB2b, StoreModal } from '@akinon/pz-b2b';
|
201
219
|
```
|
202
220
|
|
203
221
|
Add useB2b hook
|
@@ -208,14 +226,14 @@ const {
|
|
208
226
|
setOpenSelectStoreModal,
|
209
227
|
divisions,
|
210
228
|
divisionLoading,
|
211
|
-
B2bButton
|
229
|
+
B2bButton
|
212
230
|
} = useB2b();
|
213
231
|
```
|
214
232
|
|
215
233
|
Add open modal button
|
216
234
|
|
217
235
|
```javascript
|
218
|
-
<B2bButton buttonText={t(
|
236
|
+
<B2bButton buttonText={t('product.add_to_cart')} />
|
219
237
|
```
|
220
238
|
|
221
239
|
Add the end in the return
|
@@ -229,6 +247,6 @@ Add the end in the return
|
|
229
247
|
open={openSelectStoreModal}
|
230
248
|
setOpen={setOpenSelectStoreModal}
|
231
249
|
/>
|
232
|
-
)
|
250
|
+
);
|
233
251
|
}
|
234
252
|
```
|
package/package.json
CHANGED
package/src/basket-b2b/index.tsx
CHANGED
@@ -7,18 +7,22 @@ import {
|
|
7
7
|
Trans,
|
8
8
|
Price,
|
9
9
|
Icon,
|
10
|
-
Link
|
10
|
+
Link,
|
11
|
+
Button
|
11
12
|
} from '@akinon/next/components';
|
12
13
|
import {
|
13
14
|
useGetBasketB2bQuery,
|
14
15
|
useGetDraftsQuery,
|
15
|
-
useLoadBasketMutation
|
16
|
+
useLoadBasketMutation,
|
17
|
+
useGetBasketStatusMutation,
|
18
|
+
useExportBasketMutation
|
16
19
|
} from '@akinon/next/data/client/b2b';
|
17
20
|
import {
|
18
21
|
BasketItem,
|
19
22
|
RequestQuoteModal,
|
20
23
|
RemoveProductModal,
|
21
|
-
SaveCartModal
|
24
|
+
SaveCartModal,
|
25
|
+
UploadModal
|
22
26
|
} from '../views/basket-b2b';
|
23
27
|
|
24
28
|
interface BasketB2bTranslationsProps {
|
@@ -66,11 +70,14 @@ interface BasketB2bProps {
|
|
66
70
|
|
67
71
|
export function BasketB2b({ translations }: BasketB2bProps) {
|
68
72
|
const { data: basket, isLoading, isSuccess } = useGetBasketB2bQuery();
|
73
|
+
const [getBasketStatus] = useGetBasketStatusMutation();
|
74
|
+
const [exportBasket] = useExportBasketMutation();
|
69
75
|
const [loadBasket] = useLoadBasketMutation();
|
70
76
|
const { data: dataDraft } = useGetDraftsQuery();
|
71
77
|
const [open, setOpen] = useState(false);
|
72
78
|
const [openRequestQuote, setRequestQuote] = useState(false);
|
73
79
|
const [openRemoveProduct, setRemoveProduct] = useState(false);
|
80
|
+
const [openUploadBasket, setUploadBasket] = useState(false);
|
74
81
|
const [productToBeDeleted, setProductToBeDeleted] = useState(null);
|
75
82
|
const [selectedDraft, setSelectedDraft] = useState(null);
|
76
83
|
|
@@ -129,6 +136,42 @@ export function BasketB2b({ translations }: BasketB2bProps) {
|
|
129
136
|
|
130
137
|
loadBasket(selectedDraft);
|
131
138
|
};
|
139
|
+
const handleDownload = async () => {
|
140
|
+
const fieldsArray = [
|
141
|
+
'product__sku',
|
142
|
+
'quantity',
|
143
|
+
'division__erp_code',
|
144
|
+
'division__name',
|
145
|
+
'price',
|
146
|
+
'variants__Renk'
|
147
|
+
];
|
148
|
+
|
149
|
+
const queryString = new URLSearchParams(
|
150
|
+
fieldsArray.map((queryName) => ['_fields', queryName])
|
151
|
+
).toString();
|
152
|
+
|
153
|
+
try {
|
154
|
+
const data = await exportBasket(queryString).unwrap();
|
155
|
+
await handleDownloadStatus(data.cache_key);
|
156
|
+
} catch (error) {
|
157
|
+
console.error('Error during download process:', error);
|
158
|
+
}
|
159
|
+
};
|
160
|
+
|
161
|
+
const handleDownloadStatus = async (cacheKey) => {
|
162
|
+
try {
|
163
|
+
const data = await getBasketStatus(cacheKey).unwrap();
|
164
|
+
if (data.status === 'completed') {
|
165
|
+
window.location.href = data.url;
|
166
|
+
} else if (data.status === 'waiting') {
|
167
|
+
setTimeout(() => handleDownloadStatus(cacheKey), 2000);
|
168
|
+
} else {
|
169
|
+
console.warn(`Unhandled status: ${data.status}`);
|
170
|
+
}
|
171
|
+
} catch (error) {
|
172
|
+
console.error('Error fetching basket data:', error);
|
173
|
+
}
|
174
|
+
};
|
132
175
|
|
133
176
|
useEffect(() => {
|
134
177
|
if (isSuccess) {
|
@@ -156,19 +199,36 @@ export function BasketB2b({ translations }: BasketB2bProps) {
|
|
156
199
|
<h2 className="text-xl lg:text-2xl font-light">
|
157
200
|
{_translations.my_cart}
|
158
201
|
</h2>
|
159
|
-
|
160
202
|
<Link href="/" className="text-xs hover:text-secondary-500">
|
161
203
|
{_translations.back_to_shopping}
|
162
204
|
</Link>
|
163
205
|
</div>
|
164
206
|
|
165
|
-
<div className="px-5 py-4 border border-b-0 border-gray-200
|
166
|
-
<
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
207
|
+
<div className="flex justify-between items-center px-5 py-4 border border-b-0 border-gray-200">
|
208
|
+
<div className=" text-xs text-[#363636]">
|
209
|
+
<Trans
|
210
|
+
i18nKey={_translations.table.basket_qty}
|
211
|
+
components={{
|
212
|
+
Qty: <span>{basket.total_quantity}</span>
|
213
|
+
}}
|
214
|
+
/>
|
215
|
+
</div>
|
216
|
+
<div className="flex items-center gap-2">
|
217
|
+
<Button
|
218
|
+
className="flex flex-col items-center justify-center"
|
219
|
+
onClick={() => setUploadBasket(true)}
|
220
|
+
>
|
221
|
+
<span>XLS</span>
|
222
|
+
<Icon name="chevron-up" size={12} />
|
223
|
+
</Button>
|
224
|
+
<Button
|
225
|
+
className="flex flex-col items-center justify-center"
|
226
|
+
onClick={handleDownload}
|
227
|
+
>
|
228
|
+
<span>XLS</span>
|
229
|
+
<Icon name="chevron-down" size={12} />
|
230
|
+
</Button>
|
231
|
+
</div>
|
172
232
|
</div>
|
173
233
|
|
174
234
|
<div className="w-full overflow-x-auto">
|
@@ -322,6 +382,12 @@ export function BasketB2b({ translations }: BasketB2bProps) {
|
|
322
382
|
_translations.remove_product_modal.delete_product_button
|
323
383
|
}
|
324
384
|
/>
|
385
|
+
|
386
|
+
<UploadModal
|
387
|
+
setOpen={setUploadBasket}
|
388
|
+
open={openUploadBasket}
|
389
|
+
modalTitle="File Upload"
|
390
|
+
/>
|
325
391
|
</div>
|
326
392
|
</>
|
327
393
|
);
|
@@ -0,0 +1,106 @@
|
|
1
|
+
'use client';
|
2
|
+
|
3
|
+
import { useUploadFileMutation } from '@akinon/next/data/client/b2b';
|
4
|
+
import React, { useState } from 'react';
|
5
|
+
import { Button } from '@akinon/next/components';
|
6
|
+
import { useLocalization } from '@akinon/next/hooks';
|
7
|
+
|
8
|
+
export const UploadModal = ({
|
9
|
+
open,
|
10
|
+
setOpen,
|
11
|
+
modalTitle
|
12
|
+
}: {
|
13
|
+
open: boolean;
|
14
|
+
setOpen: (open: boolean) => void;
|
15
|
+
modalTitle: string;
|
16
|
+
}) => {
|
17
|
+
const { t } = useLocalization();
|
18
|
+
const [file, setFile] = useState<File | null>(null);
|
19
|
+
const [uploadFile, { isLoading }] = useUploadFileMutation();
|
20
|
+
const [error, setError] = useState<string | null>(null);
|
21
|
+
|
22
|
+
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
23
|
+
setFile(e.target.files?.[0] || null);
|
24
|
+
setError(null);
|
25
|
+
};
|
26
|
+
|
27
|
+
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
28
|
+
e.preventDefault();
|
29
|
+
|
30
|
+
if (!file) {
|
31
|
+
setError(t('basket.b2b.upload_file_modal.error.no_file_selected'));
|
32
|
+
return;
|
33
|
+
}
|
34
|
+
|
35
|
+
const formData = new FormData();
|
36
|
+
formData.append('filename', file);
|
37
|
+
|
38
|
+
try {
|
39
|
+
await uploadFile(formData).unwrap();
|
40
|
+
setOpen(false);
|
41
|
+
setFile(null);
|
42
|
+
setError(null);
|
43
|
+
} catch (uploadError) {
|
44
|
+
setError(t('basket.b2b.upload_file_modal.error.upload_failed'));
|
45
|
+
}
|
46
|
+
};
|
47
|
+
|
48
|
+
if (!open) return null;
|
49
|
+
|
50
|
+
return (
|
51
|
+
<div className="before:w-full before:h-screen before:content-[''] before:bg-black/60 before:z-40 before:fixed before:inset-0">
|
52
|
+
<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">
|
53
|
+
<header className="relative border-b border-[#d5dadb]">
|
54
|
+
<h3 className="text-left px-[30px] py-4 font-medium text-lg">
|
55
|
+
{modalTitle}
|
56
|
+
</h3>
|
57
|
+
<button
|
58
|
+
className="absolute right-[30px] top-5"
|
59
|
+
onClick={() => setOpen(false)}
|
60
|
+
>
|
61
|
+
<i className="pz-icon-close"></i>
|
62
|
+
</button>
|
63
|
+
</header>
|
64
|
+
<div className="p-[30px]">
|
65
|
+
<form onSubmit={handleSubmit}>
|
66
|
+
<div className="relative flex flex-col justify-center items-center mb-4">
|
67
|
+
<input
|
68
|
+
type="file"
|
69
|
+
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
|
70
|
+
onChange={handleFileChange}
|
71
|
+
className="opacity-0 w-full absolute left-0 top-0 h-full text-[0px] cursor-pointer"
|
72
|
+
/>
|
73
|
+
<Button className="pointer-events-none h-9 text-white border rounded mb-1">
|
74
|
+
{t('basket.b2b.upload_file_modal.select_file')}
|
75
|
+
</Button>
|
76
|
+
|
77
|
+
<p className="text-xs text-[#3e3e3e]">
|
78
|
+
{file
|
79
|
+
? file.name
|
80
|
+
: t('basket.b2b.upload_file_modal.no_file_selected')}
|
81
|
+
</p>
|
82
|
+
</div>
|
83
|
+
|
84
|
+
{error && (
|
85
|
+
<p className="text-xs text-center text-error mb-4">{error}</p>
|
86
|
+
)}
|
87
|
+
|
88
|
+
<Button
|
89
|
+
type="submit"
|
90
|
+
className="h-8 bg-black text-white font-medium text-sm w-full"
|
91
|
+
disabled={isLoading}
|
92
|
+
>
|
93
|
+
{isLoading ? (
|
94
|
+
<div className="w-full h-full flex justify-center items-center">
|
95
|
+
<div className="w-4 h-4 border-2 border-gray-600 border-t-transparent rounded-full animate-spin" />
|
96
|
+
</div>
|
97
|
+
) : (
|
98
|
+
t('basket.b2b.upload_file_modal.upload')
|
99
|
+
)}
|
100
|
+
</Button>
|
101
|
+
</form>
|
102
|
+
</div>
|
103
|
+
</div>
|
104
|
+
</div>
|
105
|
+
);
|
106
|
+
};
|