@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 +51 -3
- package/package.json +1 -1
- package/src/api/product-export.controller.d.ts +1 -1
- package/src/api/product-export.controller.js +4 -8
- package/src/api/product-export.controller.js.map +1 -1
- package/src/dashboard/export.tsx +337 -0
- package/src/dashboard/import.tsx +634 -0
- package/src/dashboard/index.tsx +17 -0
- package/src/dashboard/locales/en.json +41 -0
- package/src/dashboard/locales/sv.json +41 -0
- package/src/dashboard/utils.ts +64 -0
- package/src/product-import-export.plugin.js +2 -0
- package/src/product-import-export.plugin.js.map +1 -1
- package/src/providers/import-providers/product-importer.d.ts +3 -1
- package/src/providers/import-providers/product-importer.js +41 -17
- package/src/providers/import-providers/product-importer.js.map +1 -1
- package/src/services/product-export.service.js +9 -8
- package/src/services/product-export.service.js.map +1 -1
- package/src/types.d.ts +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,16 +1,64 @@
|
|
|
1
|
-
## 3.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.
|
|
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:**
|
|
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
|
@@ -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,
|
|
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,
|
|
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
|
|
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.
|
|
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,
|
|
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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,wCAAuD;AACvD,2CASuB;AAEvB,+EAAyE;AACzE,uCAAwB;AACxB,2BAA2C;AAC3C,4CAAmE;AAI5D,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;IAEsB;IAC9C;IAFV,YACwD,OAA0B,EACxE,oBAA0C;QADI,YAAO,GAAP,OAAO,CAAmB;QACxE,yBAAoB,GAApB,oBAAoB,CAAsB;IACjD,CAAC;IAGE,AAAN,KAAK,CAAC,cAAc,CACX,GAAmB,EACnB,GAAa,EACD,QAAgB,EACZ,YAAoB,EAClB,cAA8B,EACxB,oBAA4B,
|
|
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
|
+
}
|