@akinon/pz-b2b 1.60.0 → 1.61.0-rc.22

Sign up to get free protection for your applications and to get access to all the features.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # @akinon/pz-b2b
2
2
 
3
+ ## 1.61.0-rc.22
4
+
5
+ ### Minor Changes
6
+
7
+ - 64699d3f: ZERO-2761: Fix invalid import for plugin module
8
+ - c45b62c9: ZERO-2818: Add upload and download support for b2b package
9
+
3
10
  ## 1.60.0
4
11
 
5
12
  ## 1.59.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.60.0",
3
+ "version": "1.61.0-rc.22",
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
+ };