@akinon/pz-b2b 1.62.0 → 1.63.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @akinon/pz-b2b
2
2
 
3
+ ## 1.63.0
4
+
5
+ ### Minor Changes
6
+
7
+ - c45b62c: ZERO-2818: Add upload and download support for b2b package
8
+
3
9
  ## 1.62.0
4
10
 
5
11
  ## 1.61.0
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 = ["...", "pz-b2b"];
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 "@akinon/pz-b2b";
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("product.add_to_cart")} />
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akinon/pz-b2b",
3
- "version": "1.62.0",
3
+ "version": "1.63.0",
4
4
  "license": "MIT",
5
5
  "main": "src/index.ts",
6
6
  "peerDependencies": {
@@ -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 text-xs text-[#363636]">
166
- <Trans
167
- i18nKey={_translations.table.basket_qty}
168
- components={{
169
- Qty: <span>{basket.total_quantity}</span>
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
  );
@@ -2,3 +2,4 @@ export * from './basket-item';
2
2
  export * from './remove-product-modal';
3
3
  export * from './request-quote-modal';
4
4
  export * from './save-cart-modal';
5
+ export * from './upload-modal';
@@ -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
+ };