@haus-tech/product-import-export-plugin 3.0.26-0 → 3.0.28-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,16 +1,64 @@
1
- ## 3.0.25-0
1
+ ## 3.0.27-4
2
2
 
3
3
  This was a version bump only for product-import-export-plugin to align it with other projects, there were no code changes.
4
4
 
5
- ## 3.0.24-0
5
+ ## 3.0.27-3
6
+
7
+ This was a version bump only for product-import-export-plugin to align it with other projects, there were no code changes.
8
+
9
+ ## 3.0.27-2
6
10
 
7
11
  This was a version bump only for product-import-export-plugin to align it with other projects, there were no code changes.
8
12
 
13
+ ## 3.0.27-1
14
+
15
+ This was a version bump only for product-import-export-plugin to align it with other projects, there were no code changes.
16
+
17
+ ## 3.0.27-0
18
+
19
+ ### 🚀 Features
20
+
21
+ - **product-import-export-plugin:** add restoreSoftDeleted option for product import ([0458a2a](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/commit/0458a2a))
22
+ - **product-importer:** enhance product variant handling ([4af2198](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/commit/4af2198))
23
+ - **product-import-export-plugin:** enhance product import service with event bus integration and collection filtering ([#48](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/pull/48))
24
+ - **product-import-export-plugin:** implement removal of option groups from product ([39f34ec](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/commit/39f34ec))
25
+ - Enhance asset importer with URL and local file stream handling ([2b5ae3d](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/commit/2b5ae3d))
26
+
27
+ ### 🩹 Fixes
28
+
29
+ - **product-import-export-plugin:** improve import parser handling ([c952c7b](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/commit/c952c7b))
30
+ - **product-import-export-plugin:** improve variant enabled handling ([a3a6af3](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/commit/a3a6af3))
31
+ - **product-import-export-plugin:** improve option group handling and validate custom fields ([a95db85](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/commit/a95db85))
32
+ - **product-import-export-plugin:** handle undefined prices in product variants ([#49](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/pull/49))
33
+ - Refactor facetValues and options mapping for consistency in extended fast importer ([3514892](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/commit/3514892))
34
+ - Improve asset and product importer logic for better channel handling and data consistency ([bc9416e](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/commit/bc9416e))
35
+ - Assign assets to current channel on import ([#41](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/pull/41))
36
+
37
+ ## 3.0.24-0
38
+
39
+ ### 🚀 Features
40
+
41
+ - **product-import-export-plugin:** add restoreSoftDeleted option for product import ([0458a2a](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/commit/0458a2a))
42
+ - **product-importer:** enhance product variant handling ([4af2198](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/commit/4af2198))
43
+ - **product-import-export-plugin:** enhance product import service with event bus integration and collection filtering ([#48](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/pull/48))
44
+ - **product-import-export-plugin:** implement removal of option groups from product ([39f34ec](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/commit/39f34ec))
45
+ - Enhance asset importer with URL and local file stream handling ([2b5ae3d](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/commit/2b5ae3d))
46
+
47
+ ### 🩹 Fixes
48
+
49
+ - **product-import-export-plugin:** improve import parser handling ([c952c7b](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/commit/c952c7b))
50
+ - **product-import-export-plugin:** improve variant enabled handling ([a3a6af3](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/commit/a3a6af3))
51
+ - **product-import-export-plugin:** improve option group handling and validate custom fields ([a95db85](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/commit/a95db85))
52
+ - **product-import-export-plugin:** handle undefined prices in product variants ([#49](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/pull/49))
53
+ - Refactor facetValues and options mapping for consistency in extended fast importer ([3514892](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/commit/3514892))
54
+ - Improve asset and product importer logic for better channel handling and data consistency ([bc9416e](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/commit/bc9416e))
55
+ - Assign assets to current channel on import ([#41](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/pull/41))
56
+
9
57
  ## 3.0.23-0
10
58
 
11
59
  ### 🚀 Features
12
60
 
13
- - **product-import-export-plugin:** enhance product export functionality ([ca68282](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/commit/ca68282))
61
+ - **product-import-export-plugin:** add restoreSoftDeleted option for product import ([0458a2a](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/commit/0458a2a))
14
62
  - **product-importer:** enhance product variant handling ([4af2198](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/commit/4af2198))
15
63
  - **product-import-export-plugin:** enhance product import service with event bus integration and collection filtering ([#48](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/pull/48))
16
64
  - **product-import-export-plugin:** implement removal of option groups from product ([39f34ec](https://github.com/WeAreHausTech/haus-tech-vendure-plugins/commit/39f34ec))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haus-tech/product-import-export-plugin",
3
- "version": "3.0.26-0",
3
+ "version": "3.0.28-0",
4
4
  "description": "A Vendure plugin for importing and exporting products using a CSV file",
5
5
  "author": "Haus Tech",
6
6
  "repository": {
@@ -6,7 +6,7 @@ export declare class ProductExportController {
6
6
  private options;
7
7
  private productExportService;
8
8
  constructor(options: PluginInitOptions, productExportService: ProductExportService);
9
- exportProducts(ctx: RequestContext, res: Response, fileName: string, customFields: string, exportAssetsAs: 'url' | 'json', selectedExportFields: string, pageSize: string | undefined, concurrency: string | undefined, selection: ID[]): Promise<void>;
9
+ exportProducts(ctx: RequestContext, res: Response, fileName: string, customFields: string, exportAssetsAs: 'url' | 'json', selectedExportFields: string, selection: ID[]): Promise<void>;
10
10
  getCustomFields(ctx: RequestContext, ids: string[]): Promise<{
11
11
  name: string;
12
12
  type: string;
@@ -59,7 +59,7 @@ let ProductExportController = class ProductExportController {
59
59
  this.options = options;
60
60
  this.productExportService = productExportService;
61
61
  }
62
- async exportProducts(ctx, res, fileName, customFields, exportAssetsAs, selectedExportFields, pageSize = '50', concurrency = '5', selection) {
62
+ async exportProducts(ctx, res, fileName, customFields, exportAssetsAs, selectedExportFields, selection) {
63
63
  try {
64
64
  if (!selection ||
65
65
  !Array.isArray(selection) ||
@@ -79,9 +79,7 @@ let ProductExportController = class ProductExportController {
79
79
  else if (!fileName.endsWith('.csv')) {
80
80
  fileName += '.csv';
81
81
  }
82
- const pageSizeNum = pageSize ? Math.max(1, parseInt(pageSize, 10)) : 50;
83
- const concurrencyNum = concurrency ? Math.max(1, parseInt(concurrency, 10)) : 5;
84
- const csv = await this.productExportService.createExportFile(ctx, selection, fileName, customFields, exportAssetsAs, selectedExportFields, pageSizeNum, concurrencyNum);
82
+ const csv = await this.productExportService.createExportFile(ctx, selection, fileName, customFields, exportAssetsAs, selectedExportFields);
85
83
  const readStream = fs.createReadStream(csv);
86
84
  res.set({
87
85
  'Content-Type': 'text/csv',
@@ -109,11 +107,9 @@ __decorate([
109
107
  __param(3, (0, common_1.Query)('customFields')),
110
108
  __param(4, (0, common_1.Query)('exportAssetsAs')),
111
109
  __param(5, (0, common_1.Query)('selectedExportFields')),
112
- __param(6, (0, common_1.Query)('pageSize')),
113
- __param(7, (0, common_1.Query)('concurrency')),
114
- __param(8, (0, common_1.Body)()),
110
+ __param(6, (0, common_1.Body)()),
115
111
  __metadata("design:type", Function),
116
- __metadata("design:paramtypes", [core_1.RequestContext, Object, String, String, String, String, Object, Object, Array]),
112
+ __metadata("design:paramtypes", [core_1.RequestContext, Object, String, String, String, String, Array]),
117
113
  __metadata("design:returntype", Promise)
118
114
  ], ProductExportController.prototype, "exportProducts", null);
119
115
  __decorate([
@@ -1 +1 @@
1
- {"version":3,"file":"product-export.controller.js","sourceRoot":"","sources":["../../../../../packages/product-import-export-plugin/src/api/product-export.controller.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"product-export.controller.js","sourceRoot":"","sources":["../../../../../packages/product-import-export-plugin/src/api/product-export.controller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,337 @@
1
+ import {
2
+ Button,
3
+ Dialog,
4
+ DialogContent,
5
+ DialogDescription,
6
+ DialogFooter,
7
+ DialogHeader,
8
+ DialogTitle,
9
+ Input,
10
+ Label,
11
+ RadioGroup,
12
+ RadioGroupItem,
13
+ DataTableBulkActionItem,
14
+ } from '@vendure/dashboard'
15
+ import { DownloadIcon } from 'lucide-react'
16
+ import { useState, useEffect } from 'react'
17
+ import { toast } from 'sonner'
18
+ import {
19
+ getServerLocation,
20
+ getChannelHeader,
21
+ type PluginInitOptions,
22
+ type ExportFields,
23
+ type ProductFields,
24
+ type VariantFields,
25
+ } from './utils'
26
+
27
+ // Export Dialog Component
28
+ function ExportDialog({
29
+ open,
30
+ onOpenChange,
31
+ productIds,
32
+ }: {
33
+ open: boolean
34
+ onOpenChange: (open: boolean) => void
35
+ productIds: string[]
36
+ }) {
37
+ const [fileName, setFileName] = useState('')
38
+ const [customFields, setCustomFields] = useState<string[]>([])
39
+ const [selectedFields, setSelectedFields] = useState<string[]>([])
40
+ const [exportAssetsAs, setExportAssetsAs] = useState<'url' | 'json'>('url')
41
+ const [selectedExportFields, setSelectedExportFields] = useState<ExportFields>([])
42
+ const [availableExportFields] = useState<ExportFields>([
43
+ 'productId',
44
+ 'name',
45
+ 'slug',
46
+ 'description',
47
+ 'assets',
48
+ 'facets',
49
+ 'optionGroups',
50
+ 'sku',
51
+ 'optionValues',
52
+ 'price',
53
+ 'taxCategory',
54
+ 'stockOnHand',
55
+ 'trackInventory',
56
+ 'variantAssets',
57
+ 'variantFacets',
58
+ 'enabled',
59
+ ])
60
+ const [config, setConfig] = useState<PluginInitOptions['exportOptions'] | null>(null)
61
+ const [toggleAllChecked, setToggleAllChecked] = useState(false)
62
+
63
+ useEffect(() => {
64
+ const fetchConfig = async () => {
65
+ try {
66
+ const serverPath = getServerLocation()
67
+ const [configRes, customFieldsRes] = await Promise.all([
68
+ fetch(`${serverPath}/product-import-export/config`, {
69
+ method: 'GET',
70
+ headers: {
71
+ 'Content-Type': 'application/json',
72
+ ...getChannelHeader(),
73
+ },
74
+ credentials: 'include',
75
+ }),
76
+ fetch(`${serverPath}/product-export/custom-fields`, {
77
+ method: 'POST',
78
+ headers: {
79
+ 'Content-Type': 'application/json',
80
+ ...getChannelHeader(),
81
+ },
82
+ credentials: 'include',
83
+ body: JSON.stringify(productIds),
84
+ }),
85
+ ])
86
+
87
+ const configData = await configRes.json()
88
+ const customFieldsData = await customFieldsRes.json()
89
+ setConfig(configData.exportOptions)
90
+ setCustomFields(customFieldsData.map((field: { name: string }) => field.name))
91
+ setSelectedFields([...customFieldsData.map((field: { name: string }) => field.name)])
92
+ setExportAssetsAs(
93
+ configData.exportOptions?.defaultExportAssetsAs ||
94
+ configData.exportOptions?.exportAssetsAsOptions?.[0] ||
95
+ 'url',
96
+ )
97
+ if (
98
+ configData.exportOptions?.defaultExportFields ||
99
+ configData.exportOptions?.requiredExportFields
100
+ ) {
101
+ setSelectedExportFields([
102
+ ...(configData.exportOptions.defaultExportFields || []),
103
+ ...(configData.exportOptions.requiredExportFields || []),
104
+ ])
105
+ }
106
+ } catch (error) {
107
+ console.error('Failed to fetch config:', error)
108
+ }
109
+ }
110
+
111
+ if (open && productIds.length > 0) {
112
+ fetchConfig()
113
+ }
114
+ }, [open, productIds])
115
+
116
+ const toggleFieldSelection = (fieldName: string) => {
117
+ setSelectedFields((prev) =>
118
+ prev.includes(fieldName) ? prev.filter((f) => f !== fieldName) : [...prev, fieldName],
119
+ )
120
+ }
121
+
122
+ const toggleExportFieldSelection = (fieldName: ProductFields | VariantFields) => {
123
+ setSelectedExportFields((prev) =>
124
+ prev.includes(fieldName) ? prev.filter((f) => f !== fieldName) : [...prev, fieldName],
125
+ )
126
+ }
127
+
128
+ const toggleSelectAll = (checked: boolean) => {
129
+ setToggleAllChecked(checked)
130
+ if (checked) {
131
+ setSelectedExportFields([...availableExportFields])
132
+ setSelectedFields([...customFields])
133
+ } else {
134
+ setSelectedExportFields([...(config?.requiredExportFields || [])])
135
+ setSelectedFields([])
136
+ }
137
+ }
138
+
139
+ const handleExport = async () => {
140
+ const trimmedFileName = fileName.trim()
141
+ const finalFileName = trimmedFileName || config?.defaultFileName || 'products_export.csv'
142
+
143
+ toast.info(`Exporting ${productIds.length} products to ${finalFileName}`)
144
+
145
+ try {
146
+ const serverPath = getServerLocation()
147
+ const res = await fetch(
148
+ `${serverPath}/product-export/export?fileName=${encodeURIComponent(finalFileName)}&customFields=${encodeURIComponent(selectedFields.join(','))}&exportAssetsAs=${exportAssetsAs}&selectedExportFields=${encodeURIComponent(selectedExportFields.join(','))}`,
149
+ {
150
+ method: 'POST',
151
+ headers: {
152
+ 'Content-Type': 'application/json',
153
+ ...getChannelHeader(),
154
+ },
155
+ credentials: 'include',
156
+ body: JSON.stringify(productIds),
157
+ },
158
+ )
159
+
160
+ if (!res.ok) {
161
+ const json = await res.json()
162
+ throw new Error(json?.message || 'Export failed')
163
+ }
164
+
165
+ console.log('res', res)
166
+ const header = res.headers.get('Content-Disposition')
167
+
168
+ const parts = header!.split(';')
169
+ const filename = parts[1].split('=')[1]
170
+ const blob = await res.blob()
171
+
172
+ const blobUrl = window.URL.createObjectURL(blob)
173
+ const a = document.createElement('a')
174
+ document.body.appendChild(a)
175
+ a.setAttribute('hidden', 'true')
176
+ a.href = blobUrl
177
+ a.download = filename
178
+ a.setAttribute('target', '_blank')
179
+ a.click()
180
+
181
+ onOpenChange(false)
182
+ toast.success('Products exported successfully')
183
+ } catch (error: any) {
184
+ console.error(error)
185
+ toast.error(error.message || 'Failed to export products')
186
+ }
187
+ }
188
+
189
+ return (
190
+ <Dialog open={open} onOpenChange={onOpenChange}>
191
+ <DialogContent className="max-w-2xl max-h-[80vh] overflow-y-auto">
192
+ <DialogHeader>
193
+ <DialogTitle>Export products to CSV</DialogTitle>
194
+ <DialogDescription>Configure export options</DialogDescription>
195
+ </DialogHeader>
196
+
197
+ <div className="space-y-4">
198
+ <div>
199
+ <Label htmlFor="file-name">File name (optional)</Label>
200
+ <Input
201
+ id="file-name"
202
+ value={fileName}
203
+ onChange={(e) => setFileName(e.target.value)}
204
+ placeholder={config?.defaultFileName || 'products_export.csv'}
205
+ className="mt-1"
206
+ />
207
+ </div>
208
+
209
+ <div className="flex items-center space-x-2">
210
+ <input
211
+ type="checkbox"
212
+ id="select-all"
213
+ checked={toggleAllChecked}
214
+ onChange={(e) => toggleSelectAll(e.target.checked)}
215
+ />
216
+ <Label htmlFor="select-all" className="font-normal">
217
+ Select all
218
+ </Label>
219
+ </div>
220
+
221
+ <div>
222
+ <Label>Fields to export</Label>
223
+ <div className="grid grid-cols-2 gap-2 mt-2">
224
+ {availableExportFields.map((field) => (
225
+ <div key={field} className="flex items-center space-x-2">
226
+ <input
227
+ type="checkbox"
228
+ id={`field-${field}`}
229
+ checked={selectedExportFields.includes(field)}
230
+ onChange={() => toggleExportFieldSelection(field)}
231
+ disabled={config?.requiredExportFields?.includes(field)}
232
+ />
233
+ <Label htmlFor={`field-${field}`} className="font-normal">
234
+ {field}
235
+ </Label>
236
+ </div>
237
+ ))}
238
+ </div>
239
+ </div>
240
+
241
+ {customFields.length > 0 && (
242
+ <div>
243
+ <Label>Custom fields to include in the export</Label>
244
+ <div className="grid grid-cols-2 gap-2 mt-2">
245
+ {customFields.map((field) => (
246
+ <div key={field} className="flex items-center space-x-2">
247
+ <input
248
+ type="checkbox"
249
+ id={`custom-${field}`}
250
+ checked={selectedFields.includes(field)}
251
+ onChange={() => toggleFieldSelection(field)}
252
+ />
253
+ <Label htmlFor={`custom-${field}`} className="font-normal">
254
+ {field}
255
+ </Label>
256
+ </div>
257
+ ))}
258
+ </div>
259
+ </div>
260
+ )}
261
+
262
+ <div>
263
+ <Label>Export assets as:</Label>
264
+ <RadioGroup
265
+ value={exportAssetsAs}
266
+ onValueChange={(val) => setExportAssetsAs(val as 'url' | 'json')}
267
+ >
268
+ {config?.exportAssetsAsOptions?.includes('url') && (
269
+ <div className="flex items-center space-x-2">
270
+ <RadioGroupItem value="url" id="assets-url" />
271
+ <Label htmlFor="assets-url" className="font-normal">
272
+ URL
273
+ </Label>
274
+ </div>
275
+ )}
276
+ {config?.exportAssetsAsOptions?.includes('json') && (
277
+ <div className="flex items-center space-x-2">
278
+ <RadioGroupItem value="json" id="assets-json" />
279
+ <Label htmlFor="assets-json" className="font-normal">
280
+ JSON
281
+ </Label>
282
+ </div>
283
+ )}
284
+ </RadioGroup>
285
+ </div>
286
+ </div>
287
+
288
+ <DialogFooter>
289
+ <Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
290
+ Cancel
291
+ </Button>
292
+ <Button type="button" onClick={handleExport}>
293
+ Export
294
+ </Button>
295
+ </DialogFooter>
296
+ </DialogContent>
297
+ </Dialog>
298
+ )
299
+ }
300
+
301
+ // Export Bulk Action Component for Product List
302
+ export function ExportProductsBulkAction({
303
+ selection,
304
+ table,
305
+ }: {
306
+ selection: Array<{ id: string }>
307
+ table: any
308
+ }) {
309
+ const [dialogOpen, setDialogOpen] = useState(false)
310
+ const productIds = selection.map((product) => product.id)
311
+
312
+ const handleClose = () => {
313
+ setDialogOpen(false)
314
+ table.resetRowSelection()
315
+ }
316
+
317
+ return (
318
+ <>
319
+ <DataTableBulkActionItem
320
+ onClick={() => setDialogOpen(true)}
321
+ label="Export products to CSV"
322
+ icon={DownloadIcon}
323
+ />
324
+ <ExportDialog
325
+ open={dialogOpen}
326
+ onOpenChange={(open) => {
327
+ if (open) {
328
+ setDialogOpen(true)
329
+ } else {
330
+ handleClose()
331
+ }
332
+ }}
333
+ productIds={productIds}
334
+ />
335
+ </>
336
+ )
337
+ }