@haus-tech/product-import-export-plugin 3.3.10-next.1 β†’ 3.3.10-next.3

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,30 +1,8 @@
1
- ## 3.3.10-next.0
2
-
3
- This was a version bump only for product-import-export-plugin to align it with other projects, there were no code changes.
4
-
5
- ## 3.3.2-next.5
6
-
7
- ### πŸš€ Features
8
-
9
- - **product-import-export-plugin:** enhance product export service with stock level handling ([2d99696](https://github.com/WeAreHausTech/haus-tech-vendure-plugins-public/commit/2d99696))
10
-
11
- ## 3.3.2-next.3
12
-
13
- ### πŸš€ Features
14
-
15
- - **product-import-export-plugin:** add popover help for export asset options ([7aa8373](https://github.com/WeAreHausTech/haus-tech-vendure-plugins-public/commit/7aa8373))
16
- - **product-import-export-plugin:** implement export configuration panel and enhance export dialog ([d2e1f34](https://github.com/WeAreHausTech/haus-tech-vendure-plugins-public/commit/d2e1f34))
17
- - **product-import-export-plugin:** enhance product export functionality and update tests ([de56d44](https://github.com/WeAreHausTech/haus-tech-vendure-plugins-public/commit/de56d44))
18
- - **product-import-export-plugin:** update dependencies and enhance import/export functionality ([9c78665](https://github.com/WeAreHausTech/haus-tech-vendure-plugins-public/commit/9c78665))
19
- - **product-import-export-plugin:** enhance email notification and import/export functionality ([c133dc5](https://github.com/WeAreHausTech/haus-tech-vendure-plugins-public/commit/c133dc5))
20
- - **product-import-export-plugin:** enforce import/export acceptance criteria and enhance documentation ([b1a93b6](https://github.com/WeAreHausTech/haus-tech-vendure-plugins-public/commit/b1a93b6))
21
- - **product-import-export-plugin:** update pre-version command and clean up export artifacts ([1cde024](https://github.com/WeAreHausTech/haus-tech-vendure-plugins-public/commit/1cde024))
22
- - **product-import-export-plugin:** optimize asset handling and caching in product importer ([9d2888e](https://github.com/WeAreHausTech/haus-tech-vendure-plugins-public/commit/9d2888e))
23
- - **product-import-export-plugin:** enhance testing and export functionality ([1764eb2](https://github.com/WeAreHausTech/haus-tech-vendure-plugins-public/commit/1764eb2))
1
+ ## 3.3.10-next.2
24
2
 
25
3
  ### 🩹 Fixes
26
4
 
27
- - **product-import-export-plugin:** update dashboard and export settings screenshots ([2129b39](https://github.com/WeAreHausTech/haus-tech-vendure-plugins-public/commit/2129b39))
5
+ - **product-import-export-plugin:** poll exports list and restore featured asset on import ([2c61b48](https://github.com/WeAreHausTech/haus-tech-vendure-plugins-public/commit/2c61b48))
28
6
 
29
7
  ## 3.3.2-next.2
30
8
 
package/README.mdx CHANGED
@@ -19,16 +19,16 @@ A Vendure plugin for importing and exporting product data via CSV. Supports bulk
19
19
 
20
20
  ## Features
21
21
 
22
- - **Import** – Bulk upload products from CSV with validation, custom fields, assets, facets, and variants
23
- - **Export** – Export products to CSV with selected fields; export all products or a selection
24
- - **Storage strategies** – Store exported files in S3 or locally on disk (with automatic fallback)
25
- - **UI** – Admin UI and Dashboard integration for import and export
22
+ * **Import** – Bulk upload products from CSV with validation, custom fields, assets, facets, and variants
23
+ * **Export** – Export products to CSV with selected fields; export all products or a selection
24
+ * **Storage strategies** – Store exported files in S3 or locally on disk (with automatic fallback)
25
+ * **UI** – Admin UI and Dashboard integration for import and export
26
26
 
27
27
  ![Dashboard UI](assets/Screenshot-dashboard-ui.png)
28
28
 
29
29
  ## Compatibility
30
30
 
31
- Vendure **^3.6.0**
31
+ Vendure **^2.0.0** or **^3.0.0**
32
32
 
33
33
  ## Getting started
34
34
 
@@ -65,13 +65,13 @@ export const config = {
65
65
 
66
66
  ### Import options
67
67
 
68
- | Option | Type | Default | Description |
69
- | ------------------------ | -------------------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------- |
70
- | `updateProductSlug` | `boolean` | `true` | Update product slug from the `name` field in the CSV |
71
- | `restoreSoftDeleted` | `boolean` | `true` | Restore soft-deleted products/variants found by id or SKU during import. If `false`, new products will be created instead. |
72
- | `storageStrategy` | `ImportJobStorageStrategy` | local disk | Strategy instance for queued import file storage and retrieval. |
73
- | `storageStrategyFactory` | `(injector: Injector) => ImportJobStorageStrategy` | `undefined` | Factory for DI-aware strategy creation (recommended for S3/remote storage). |
74
- | `importJobStorage` | `'local' \| 's3'` | `'local'` | Deprecated compatibility flag. Prefer `storageStrategy` / `storageStrategyFactory`. |
68
+ | Option | Type | Default | Description |
69
+ | ------------------------ | ----------------------------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------- |
70
+ | `updateProductSlug` | `boolean` | `true` | Update product slug from the `name` field in the CSV |
71
+ | `restoreSoftDeleted` | `boolean` | `true` | Restore soft-deleted products/variants found by id or SKU during import. If `false`, new products will be created instead. |
72
+ | `storageStrategy` | `ImportJobStorageStrategy` | local disk | Strategy instance for queued import file storage and retrieval. |
73
+ | `storageStrategyFactory` | `(injector: Injector) => ImportJobStorageStrategy` | `undefined` | Factory for DI-aware strategy creation (recommended for S3/remote storage). |
74
+ | `importJobStorage` | `'local' \| 's3'` | `'local'` | Deprecated compatibility flag. Prefer `storageStrategy` / `storageStrategyFactory`. |
75
75
 
76
76
  Available import storage strategies:
77
77
 
@@ -94,8 +94,8 @@ Available import storage strategies:
94
94
 
95
95
  The plugin supports configurable storage for exported files:
96
96
 
97
- - **S3** – Stores export files in an S3-compatible bucket
98
- - **Disk (default)** – Saves files locally on the server
97
+ * **S3** – Stores export files in an S3-compatible bucket
98
+ * **Disk (default)** – Saves files locally on the server
99
99
 
100
100
  ## CSV Format for Import
101
101
 
@@ -106,21 +106,21 @@ The plugin supports configurable storage for exported files:
106
106
 
107
107
  ### Optional columns
108
108
 
109
- | Column | Format | Example |
110
- | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
111
- | `name`, `slug`, `description` | Text, or `name:en`, `name:sv` for translations | `My Product` |
112
- | `assets` | Pipe-separated URLs, or JSON `[{"url":"...","name":"..."}]` | `https://example.com/1.jpg\|https://example.com/2.jpg` |
113
- | `facets` | `facet:value` pairs, pipe-separated | `Brand:Acme\|Category:Electronics` |
114
- | `optionGroups` | Pipe-separated group names. Use `name:code` to share a group across products (see [Sharing option groups](#sharing-option-groups)) | `Size\|Color` or `size:shoe-size` |
115
- | `optionValues` | Pipe-separated values (order matches option groups) | `Large\|Blue` |
116
- | `price` | Number (in smallest currency unit) | `9999` |
117
- | `taxCategory` | Tax category name | `standard` |
118
- | `stockOnHand` | Number | `100` |
119
- | `trackInventory` | `true` or `false` | `true` |
120
- | `variantAssets` | Same format as `assets` | `https://example.com/variant.jpg` |
121
- | `variantFacets` | Same format as `facets` | `Material:Cotton` |
122
- | `enabled` | `true` or `false` | `true` |
123
- | Custom fields | Column name = custom field name; value depends on field type | Add columns for any Product or ProductVariant custom fields defined in your Vendure config |
109
+ | Column | Format | Example |
110
+ | ----------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------ |
111
+ | `name`, `slug`, `description` | Text, or `name:en`, `name:sv` for translations | `My Product` |
112
+ | `assets` | Pipe-separated URLs, or JSON `[{"url":"...","name":"..."}]` | `https://example.com/1.jpg\|https://example.com/2.jpg` |
113
+ | `facets` | `facet:value` pairs, pipe-separated | `Brand:Acme\|Category:Electronics` |
114
+ | `optionGroups` | Pipe-separated group names | `Size\|Color` |
115
+ | `optionValues` | Pipe-separated values (order matches option groups) | `Large\|Blue` |
116
+ | `price` | Number (in smallest currency unit) | `9999` |
117
+ | `taxCategory` | Tax category name | `standard` |
118
+ | `stockOnHand` | Number | `100` |
119
+ | `trackInventory` | `true` or `false` | `true` |
120
+ | `variantAssets` | Same format as `assets` | `https://example.com/variant.jpg` |
121
+ | `variantFacets` | Same format as `facets` | `Material:Cotton` |
122
+ | `enabled` | `true` or `false` | `true` |
123
+ | Custom fields | Column name = custom field name; value depends on field type | Add columns for any Product or ProductVariant custom fields defined in your Vendure config |
124
124
 
125
125
  #### Import behavior rules
126
126
 
@@ -128,28 +128,6 @@ The plugin supports configurable storage for exported files:
128
128
  - If a column exists in the header but the cell is empty, that field is explicitly cleared.
129
129
  - For products with more than one variant, `optionGroups` and `optionValues` are required.
130
130
 
131
- #### Sharing option groups
132
-
133
- By default, each product gets its own option groups. To **share an option group** across
134
- multiple products (e.g. a common "Size" group for all shoes), use the `name:code` syntax in the
135
- `optionGroups` column: `size:shoe-size`. All products that reference the same code will share
136
- a single option group. Products using a plain name (e.g. just `size`) will each get their own
137
- independent option group.
138
-
139
- ```csv
140
- ...optionGroups , optionValues , ...
141
- # Each shoe product shares a single "shoe-size" option group:
142
- "size:shoe-size" , Small , ...
143
- "size:shoe-size" , Medium , ...
144
- # This product gets its own independent "size" group:
145
- "size" , 4GB , ...
146
- ```
147
-
148
- The first product to reference a given code defines the full set of option values for that
149
- shared group; subsequent products may use any subset of those values. Codes are language-
150
- agnostic β€” only the `name` portion before the colon is translated per language column
151
- (`optionGroups:en`, `optionGroups:sv`, etc.).
152
-
153
131
  #### Multi-language support
154
132
 
155
133
  Use language-specific columns by suffixing the field name with the language code. The language codes must match those configured in your Vendure instance (e.g. `en`, `sv`).
@@ -216,11 +194,7 @@ Note: templates for `@vendure/email-plugin` should be valid MJML.
216
194
 
217
195
  ```typescript
218
196
  import path from 'path'
219
- import {
220
- DefaultEmailPlugin,
221
- FileBasedTemplateLoader,
222
- defaultEmailHandlers,
223
- } from '@vendure/email-plugin'
197
+ import { DefaultEmailPlugin, FileBasedTemplateLoader, defaultEmailHandlers } from '@vendure/email-plugin'
224
198
  import {
225
199
  withProductExportedHandler,
226
200
  withProductExportedTemplateFallback,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haus-tech/product-import-export-plugin",
3
- "version": "3.3.10-next.1",
3
+ "version": "3.3.10-next.3",
4
4
  "description": "A Vendure plugin for importing and exporting products using a CSV file",
5
5
  "author": "Haus Tech",
6
6
  "repository": {
@@ -53,13 +53,13 @@
53
53
  },
54
54
  "devDependencies": {
55
55
  "@types/fs-extra": "^11.0.4",
56
- "csv-parse": "^6.0.0",
56
+ "csv-parse": "^5.6.0",
57
57
  "csv-stringify": "^6.6.0"
58
58
  },
59
59
  "peerDependencies": {
60
- "@vendure/core": "^3.6.0",
61
- "@vendure/email-plugin": "^3.6.0",
62
- "csv-parse": "^6.0.0",
60
+ "@vendure/core": "^2.0.0 || ^3.0.0",
61
+ "@vendure/email-plugin": "^2.0.0 || ^3.0.0",
62
+ "csv-parse": "^5.5.6",
63
63
  "csv-stringify": "^6.5.1",
64
64
  "slug": "^9.1.0"
65
65
  },
@@ -22,7 +22,6 @@ export function ExportProductsBulkAction({
22
22
  return (
23
23
  <>
24
24
  <DataTableBulkActionItem
25
- closeOnClick={false}
26
25
  onClick={() => setDialogOpen(true)}
27
26
  label="Export products to CSV"
28
27
  icon={DownloadIcon}
@@ -6,18 +6,27 @@ import {
6
6
  DialogFooter,
7
7
  DialogHeader,
8
8
  DialogTitle,
9
+ Input,
10
+ Label,
11
+ RadioGroup,
12
+ RadioGroupItem,
13
+ Checkbox,
9
14
  } from '@vendure/dashboard'
10
- import { useRef, useState } from 'react'
15
+ import { useState, useEffect } from 'react'
16
+ import { toast } from 'sonner'
11
17
  import {
12
- ExportConfigurationPanel,
13
- type ExportConfigurationPanelHandle,
14
- } from './export-configuration-panel'
18
+ getServerLocation,
19
+ getChannelHeader,
20
+ type PluginInitOptions,
21
+ type ExportFields,
22
+ type ProductFields,
23
+ type VariantFields,
24
+ } from './utils'
15
25
 
16
26
  interface ExportDialogProps {
17
27
  open: boolean
18
28
  onOpenChange: (open: boolean) => void
19
- /** Omit for export-all-only dialogs; bulk export passes selected IDs. */
20
- productIds?: string[]
29
+ productIds: string[]
21
30
  isExportAll?: boolean
22
31
  }
23
32
 
@@ -27,15 +36,156 @@ export function ExportDialog({
27
36
  productIds,
28
37
  isExportAll = false,
29
38
  }: ExportDialogProps) {
30
- const panelRef = useRef<ExportConfigurationPanelHandle>(null)
31
- const [isSubmitting, setIsSubmitting] = useState(false)
39
+ const mandatoryOptionFields: Array<ProductFields | VariantFields> = ['optionGroups', 'optionValues']
40
+ const [fileName, setFileName] = useState('')
41
+ const [customFields, setCustomFields] = useState<string[]>([])
42
+ const [selectedFields, setSelectedFields] = useState<string[]>([])
43
+ const [exportAssetsAs, setExportAssetsAs] = useState<'url' | 'json'>('url')
44
+ const [selectedExportFields, setSelectedExportFields] = useState<ExportFields>([])
45
+ const [availableExportFields] = useState<ExportFields>([
46
+ 'name',
47
+ 'slug',
48
+ 'description',
49
+ 'assets',
50
+ 'facets',
51
+ 'optionGroups',
52
+ 'sku',
53
+ 'optionValues',
54
+ 'price',
55
+ 'taxCategory',
56
+ 'stockOnHand',
57
+ 'trackInventory',
58
+ 'variantAssets',
59
+ 'variantFacets',
60
+ 'enabled',
61
+ ])
62
+ const [config, setConfig] = useState<PluginInitOptions['exportOptions'] | null>(null)
63
+ const [toggleAllChecked, setToggleAllChecked] = useState(false)
64
+
65
+ useEffect(() => {
66
+ const fetchConfig = async () => {
67
+ try {
68
+ const serverPath = getServerLocation()
69
+ const [configRes, customFieldsRes] = await Promise.all([
70
+ fetch(`${serverPath}/product-import-export/config`, {
71
+ method: 'GET',
72
+ headers: {
73
+ 'Content-Type': 'application/json',
74
+ ...getChannelHeader(),
75
+ },
76
+ credentials: 'include',
77
+ }),
78
+ fetch(`${serverPath}/product-export/custom-fields`, {
79
+ method: 'POST',
80
+ headers: {
81
+ 'Content-Type': 'application/json',
82
+ ...getChannelHeader(),
83
+ },
84
+ credentials: 'include',
85
+ body: JSON.stringify(productIds.length > 0 ? productIds : []),
86
+ }),
87
+ ])
88
+
89
+ const configData = await configRes.json()
90
+ const customFieldsData = await customFieldsRes.json()
91
+
92
+ setConfig(configData.exportOptions)
93
+ setCustomFields(customFieldsData.map((field: { name: string }) => field.name))
94
+ setSelectedFields([...customFieldsData.map((field: { name: string }) => field.name)])
95
+ setExportAssetsAs(
96
+ configData.exportOptions?.defaultExportAssetsAs ||
97
+ configData.exportOptions?.exportAssetsAsOptions?.[0] ||
98
+ 'url',
99
+ )
100
+ if (
101
+ configData.exportOptions?.defaultExportFields ||
102
+ configData.exportOptions?.requiredExportFields
103
+ ) {
104
+ setSelectedExportFields(
105
+ Array.from(
106
+ new Set([
107
+ ...(configData.exportOptions.defaultExportFields || []),
108
+ ...(configData.exportOptions.requiredExportFields || []),
109
+ ...mandatoryOptionFields,
110
+ ]),
111
+ ) as ExportFields,
112
+ )
113
+ }
114
+ } catch (error) {
115
+ console.error('Failed to fetch config:', error)
116
+ }
117
+ }
118
+
119
+ if (open) {
120
+ fetchConfig()
121
+ }
122
+ }, [open, productIds])
123
+
124
+ const toggleFieldSelection = (fieldName: string) => {
125
+ setSelectedFields((prev) =>
126
+ prev.includes(fieldName) ? prev.filter((f) => f !== fieldName) : [...prev, fieldName],
127
+ )
128
+ }
129
+
130
+ const toggleExportFieldSelection = (fieldName: ProductFields | VariantFields) => {
131
+ if (mandatoryOptionFields.includes(fieldName)) {
132
+ return
133
+ }
134
+ setSelectedExportFields((prev) =>
135
+ prev.includes(fieldName) ? prev.filter((f) => f !== fieldName) : [...prev, fieldName],
136
+ )
137
+ }
138
+
139
+ const toggleSelectAll = (checked: boolean) => {
140
+ setToggleAllChecked(checked)
141
+ if (checked) {
142
+ setSelectedExportFields([...availableExportFields])
143
+ setSelectedFields([...customFields])
144
+ } else {
145
+ setSelectedExportFields([
146
+ ...(config?.requiredExportFields || []),
147
+ ...mandatoryOptionFields,
148
+ ])
149
+ setSelectedFields([])
150
+ }
151
+ }
32
152
 
33
153
  const handleExport = async () => {
34
- setIsSubmitting(true)
154
+ const trimmedFileName = fileName.trim()
155
+ const finalFileName = trimmedFileName || config?.defaultFileName || 'products_export.csv'
156
+
35
157
  try {
36
- await panelRef.current?.submitExport()
37
- } finally {
38
- setIsSubmitting(false)
158
+ const serverPath = getServerLocation()
159
+ const endpoint = isExportAll ? 'export-all' : 'export'
160
+ const body = isExportAll ? undefined : JSON.stringify(productIds)
161
+
162
+ const res = await fetch(
163
+ `${serverPath}/product-export/${endpoint}?fileName=${encodeURIComponent(finalFileName)}&customFields=${encodeURIComponent(selectedFields.join(','))}&exportAssetsAs=${exportAssetsAs}&selectedExportFields=${encodeURIComponent(selectedExportFields.join(','))}`,
164
+ {
165
+ method: 'POST',
166
+ headers: {
167
+ 'Content-Type': 'application/json',
168
+ ...getChannelHeader(),
169
+ },
170
+ credentials: 'include',
171
+ ...(body && { body }),
172
+ },
173
+ )
174
+
175
+ if (!res.ok) {
176
+ const json = await res.json()
177
+ throw new Error(json?.message || 'Export failed')
178
+ }
179
+
180
+ onOpenChange(false)
181
+ toast.success(
182
+ `Export job queued successfully. ${
183
+ isExportAll ? 'All' : productIds.length
184
+ } products will be exported.`,
185
+ )
186
+ } catch (error: unknown) {
187
+ console.error(error)
188
+ toast.error((error as Error)?.message || 'Failed to queue export')
39
189
  }
40
190
  }
41
191
 
@@ -47,21 +197,101 @@ export function ExportDialog({
47
197
  <DialogDescription>Configure export settings</DialogDescription>
48
198
  </DialogHeader>
49
199
 
50
- <ExportConfigurationPanel
51
- ref={panelRef}
52
- productIds={productIds}
53
- isExportAll={isExportAll}
54
- active={open}
55
- idPrefix="dialog-export"
56
- fieldsAccordionDefaultOpen
57
- onExportSuccess={() => onOpenChange(false)}
58
- />
200
+ <div className="space-y-4">
201
+ <div>
202
+ <Label htmlFor="file-name">File name (optional)</Label>
203
+ <p className="text-sm text-muted-foreground mt-1">
204
+ A timestamp will be automatically added to the filename to ensure uniqueness.
205
+ </p>
206
+ <Input
207
+ id="file-name"
208
+ value={fileName}
209
+ onChange={(e) => setFileName(e.target.value)}
210
+ placeholder={config?.defaultFileName || 'products_export.csv'}
211
+ className="mt-1"
212
+ />
213
+ </div>
214
+
215
+ <div>
216
+ <Label>Select fields to export</Label>
217
+ <div className="flex items-center space-x-2 mt-2">
218
+ <Checkbox
219
+ id="select-all"
220
+ checked={toggleAllChecked}
221
+ onCheckedChange={(checked) => toggleSelectAll(checked as boolean)}
222
+ />
223
+ <Label htmlFor="select-all" className="font-normal">
224
+ Select all
225
+ </Label>
226
+ </div>
227
+ <div className="grid grid-cols-2 gap-2 mt-2">
228
+ {availableExportFields.map((field) => (
229
+ <div key={field} className="flex items-center space-x-2">
230
+ <Checkbox
231
+ id={`field-${field}`}
232
+ checked={selectedExportFields.includes(field)}
233
+ onCheckedChange={() => toggleExportFieldSelection(field)}
234
+ disabled={
235
+ config?.requiredExportFields?.includes(field) ||
236
+ mandatoryOptionFields.includes(field)
237
+ }
238
+ />
239
+ <Label htmlFor={`field-${field}`} className="font-normal">
240
+ {field}
241
+ </Label>
242
+ </div>
243
+ ))}
244
+ </div>
245
+ {customFields.length > 0 && (
246
+ <div className="grid grid-cols-2 gap-2 mt-2">
247
+ {customFields.map((field) => (
248
+ <div key={field} className="flex items-center space-x-2">
249
+ <Checkbox
250
+ id={`custom-${field}`}
251
+ checked={selectedFields.includes(field)}
252
+ onCheckedChange={() => toggleFieldSelection(field)}
253
+ />
254
+ <Label htmlFor={`custom-${field}`} className="font-normal">
255
+ {field}
256
+ </Label>
257
+ </div>
258
+ ))}
259
+ </div>
260
+ )}
261
+ </div>
262
+
263
+ <div>
264
+ <Label>Export assets as:</Label>
265
+ <RadioGroup
266
+ value={exportAssetsAs}
267
+ onValueChange={(val) => setExportAssetsAs(val as 'url' | 'json')}
268
+ className="mt-2"
269
+ >
270
+ {config?.exportAssetsAsOptions?.includes('url') && (
271
+ <div className="flex items-center space-x-2">
272
+ <RadioGroupItem value="url" id="assets-url" />
273
+ <Label htmlFor="assets-url" className="font-normal">
274
+ URL
275
+ </Label>
276
+ </div>
277
+ )}
278
+ {config?.exportAssetsAsOptions?.includes('json') && (
279
+ <div className="flex items-center space-x-2">
280
+ <RadioGroupItem value="json" id="assets-json" />
281
+ <Label htmlFor="assets-json" className="font-normal">
282
+ JSON
283
+ </Label>
284
+ </div>
285
+ )}
286
+ </RadioGroup>
287
+ </div>
288
+ </div>
59
289
 
60
290
  <DialogFooter>
61
291
  <Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
62
292
  Cancel
63
293
  </Button>
64
- <Button type="button" onClick={handleExport} disabled={isSubmitting || !open}>
294
+ <Button type="button" onClick={handleExport}>
65
295
  Export
66
296
  </Button>
67
297
  </DialogFooter>
@@ -1,60 +1,25 @@
1
- import {
2
- Button,
3
- Card,
4
- CardContent,
5
- CardDescription,
6
- CardHeader,
7
- CardTitle,
8
- } from '@vendure/dashboard'
9
- import { DownloadIcon, InfoIcon } from 'lucide-react'
10
- import { useRef, useState } from 'react'
11
- import {
12
- ExportConfigurationPanel,
13
- type ExportConfigurationPanelHandle,
14
- } from './export-configuration-panel'
1
+ import { Button } from '@vendure/dashboard'
2
+ import { useState } from 'react'
3
+ import { ExportDialog } from './export-dialog'
15
4
 
16
5
  export function ProductExportBlock() {
17
- const panelRef = useRef<ExportConfigurationPanelHandle>(null)
18
- const [isExporting, setIsExporting] = useState(false)
6
+ const [dialogOpen, setDialogOpen] = useState(false)
19
7
 
20
- const handleExport = async () => {
21
- setIsExporting(true)
22
- try {
23
- await panelRef.current?.submitExport()
24
- } finally {
25
- setIsExporting(false)
26
- }
8
+ const handleExport = () => {
9
+ setDialogOpen(true)
27
10
  }
28
11
 
29
12
  return (
30
- <Card className="h-full">
31
- <CardHeader className="flex flex-row items-start gap-3 space-y-0 items-center">
32
- <div className="bg-primary/10 text-primary rounded-md p-2 flex h-9 w-9 items-center justify-center">
33
- <DownloadIcon className="h-5 w-5" />
34
- </div>
35
- <div className="space-y-1">
36
- <CardTitle className="mb-0">Export all products</CardTitle>
37
- <CardDescription className="text-xs">Export all your products to a CSV file.</CardDescription>
38
- </div>
39
- </CardHeader>
40
- <CardContent className="space-y-4">
41
- <ExportConfigurationPanel
42
- ref={panelRef}
43
- isExportAll
44
- active
45
- idPrefix="page-export"
46
- />
47
-
48
- <Button className="w-full" onClick={handleExport} disabled={isExporting}>
49
- <DownloadIcon className="mr-2 h-4 w-4" />
50
- {isExporting ? 'Queuing export…' : 'Export products'}
51
- </Button>
52
-
53
- <div className="flex items-start gap-2 text-xs text-muted-foreground">
54
- <InfoIcon className="h-3.5 w-3.5 shrink-0" />
55
- <span>You will receive an email when the export is ready to download.</span>
56
- </div>
57
- </CardContent>
58
- </Card>
13
+ <div className="space-y-4">
14
+ <div>
15
+ <Button onClick={handleExport}>Export all products</Button>
16
+ </div>
17
+ <ExportDialog
18
+ open={dialogOpen}
19
+ onOpenChange={setDialogOpen}
20
+ productIds={[]}
21
+ isExportAll={true}
22
+ />
23
+ </div>
59
24
  )
60
25
  }