@haus-tech/product-import-export-plugin 3.6.2 → 3.6.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 +38 -11
- package/README.mdx +69 -59
- package/package.json +7 -9
- package/src/api/plugin.controller.d.ts +32 -2
- package/src/api/plugin.controller.js +30 -4
- package/src/api/plugin.controller.js.map +1 -1
- package/src/api/product-export.controller.d.ts +1 -0
- package/src/api/product-export.controller.js +17 -0
- package/src/api/product-export.controller.js.map +1 -1
- package/src/api/product-import.controller.js +1 -0
- package/src/api/product-import.controller.js.map +1 -1
- package/src/dashboard/bulk-export.tsx +0 -1
- package/src/dashboard/export-dialog.tsx +285 -22
- package/src/dashboard/export.tsx +17 -52
- package/src/dashboard/exported-list.tsx +96 -176
- package/src/dashboard/import.tsx +149 -286
- package/src/dashboard/index.tsx +28 -27
- package/src/dashboard/utils.ts +20 -3
- package/src/index.d.ts +1 -1
- package/src/product-import-export.plugin.d.ts +0 -6
- package/src/product-import-export.plugin.js +25 -29
- package/src/product-import-export.plugin.js.map +1 -1
- package/src/providers/import-providers/asset-importer.js +8 -27
- package/src/providers/import-providers/asset-importer.js.map +1 -1
- package/src/providers/import-providers/import-parser.d.ts +0 -1
- package/src/providers/import-providers/import-parser.js +2 -18
- package/src/providers/import-providers/import-parser.js.map +1 -1
- package/src/providers/import-providers/product-importer.d.ts +0 -10
- package/src/providers/import-providers/product-importer.js +34 -115
- package/src/providers/import-providers/product-importer.js.map +1 -1
- package/src/services/export-storage/export-storage-strategy.d.ts +0 -8
- package/src/services/extended-fast-importer.service.js +12 -20
- package/src/services/extended-fast-importer.service.js.map +1 -1
- package/src/services/import-storage/import-job-storage-strategy.d.ts +0 -9
- package/src/services/product-export-queue.service.js +1 -1
- package/src/services/product-export-queue.service.js.map +1 -1
- package/src/services/product-export.service.d.ts +13 -7
- package/src/services/product-export.service.js +96 -43
- package/src/services/product-export.service.js.map +1 -1
- package/src/types.d.ts +29 -9
- package/src/ui/components/product-import.component.ts +1 -0
- package/README.md +0 -231
- package/src/dashboard/export-configuration-panel.tsx +0 -404
- package/src/dashboard/use-active-channel-key.ts +0 -7
|
@@ -1,35 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Button,
|
|
3
|
-
Card,
|
|
4
|
-
CardContent,
|
|
5
|
-
CardDescription,
|
|
6
|
-
CardHeader,
|
|
7
|
-
CardTitle,
|
|
8
3
|
Dialog,
|
|
9
4
|
DialogContent,
|
|
10
5
|
DialogDescription,
|
|
11
6
|
DialogFooter,
|
|
12
7
|
DialogHeader,
|
|
13
8
|
DialogTitle,
|
|
14
|
-
Input,
|
|
15
|
-
Table,
|
|
16
|
-
TableBody,
|
|
17
|
-
TableCell,
|
|
18
|
-
TableHead,
|
|
19
|
-
TableHeader,
|
|
20
|
-
TableRow,
|
|
21
|
-
toast,
|
|
22
9
|
} from '@vendure/dashboard'
|
|
23
|
-
import {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
SearchIcon,
|
|
28
|
-
TrashIcon,
|
|
29
|
-
} from 'lucide-react'
|
|
30
|
-
import { useEffect, useMemo, useState } from 'react'
|
|
31
|
-
import { useActiveChannelKey } from './use-active-channel-key'
|
|
32
|
-
import { getChannelHeader, getServerLocation } from './utils'
|
|
10
|
+
import { getServerLocation, getChannelHeader } from './utils'
|
|
11
|
+
import { useState, useEffect, useMemo } from 'react'
|
|
12
|
+
import { DownloadIcon, ChevronLeftIcon, ChevronRightIcon, TrashIcon } from 'lucide-react'
|
|
13
|
+
import { toast } from 'sonner'
|
|
33
14
|
|
|
34
15
|
interface ExportedFile {
|
|
35
16
|
fileName: string
|
|
@@ -37,14 +18,12 @@ interface ExportedFile {
|
|
|
37
18
|
created: string
|
|
38
19
|
}
|
|
39
20
|
|
|
40
|
-
const
|
|
21
|
+
const itemsPerPage = 10
|
|
41
22
|
const POLL_INTERVAL_MS = 10_000
|
|
42
23
|
|
|
43
24
|
export function ExportedList() {
|
|
44
|
-
const activeChannelId = useActiveChannelKey()
|
|
45
25
|
const [exportedFiles, setExportedFiles] = useState<ExportedFile[]>([])
|
|
46
26
|
const [error, setError] = useState<string | null>(null)
|
|
47
|
-
const [search, setSearch] = useState('')
|
|
48
27
|
const [currentPage, setCurrentPage] = useState(1)
|
|
49
28
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false)
|
|
50
29
|
const [fileToDelete, setFileToDelete] = useState<string | null>(null)
|
|
@@ -74,42 +53,27 @@ export function ExportedList() {
|
|
|
74
53
|
}
|
|
75
54
|
|
|
76
55
|
useEffect(() => {
|
|
77
|
-
if (!activeChannelId) return
|
|
78
|
-
setSearch('')
|
|
79
|
-
setCurrentPage(1)
|
|
80
56
|
void getExportFiles()
|
|
81
|
-
}, [
|
|
57
|
+
}, [])
|
|
82
58
|
|
|
83
59
|
useEffect(() => {
|
|
84
|
-
if (!activeChannelId) return
|
|
85
|
-
|
|
86
60
|
const intervalId = setInterval(() => {
|
|
87
61
|
void getExportFiles()
|
|
88
62
|
}, POLL_INTERVAL_MS)
|
|
89
63
|
|
|
90
64
|
return () => clearInterval(intervalId)
|
|
91
|
-
}, [
|
|
65
|
+
}, [])
|
|
92
66
|
|
|
93
|
-
|
|
94
|
-
let result = exportedFiles
|
|
95
|
-
if (search) {
|
|
96
|
-
const q = search.toLowerCase()
|
|
97
|
-
result = result.filter((f) => f.fileName.toLowerCase().includes(q))
|
|
98
|
-
}
|
|
99
|
-
return result
|
|
100
|
-
}, [exportedFiles, search])
|
|
101
|
-
|
|
102
|
-
const totalPages = Math.max(1, Math.ceil(filteredFiles.length / ITEMS_PER_PAGE))
|
|
67
|
+
// Pagination calculations
|
|
103
68
|
const paginatedFiles = useMemo(() => {
|
|
104
|
-
const startIndex = (currentPage - 1) *
|
|
105
|
-
|
|
106
|
-
|
|
69
|
+
const startIndex = (currentPage - 1) * itemsPerPage
|
|
70
|
+
const endIndex = startIndex + itemsPerPage
|
|
71
|
+
return exportedFiles.slice(startIndex, endIndex)
|
|
72
|
+
}, [exportedFiles, currentPage])
|
|
107
73
|
|
|
108
|
-
|
|
109
|
-
if (currentPage > totalPages) setCurrentPage(1)
|
|
110
|
-
}, [totalPages, currentPage])
|
|
74
|
+
const totalPages = Math.ceil(exportedFiles.length / itemsPerPage)
|
|
111
75
|
|
|
112
|
-
const
|
|
76
|
+
const downLoadFile = async (fileName: string) => {
|
|
113
77
|
const serverPath = getServerLocation()
|
|
114
78
|
const res = await fetch(`${serverPath}/product-export/download/${fileName}`, {
|
|
115
79
|
method: 'GET',
|
|
@@ -166,138 +130,94 @@ export function ExportedList() {
|
|
|
166
130
|
|
|
167
131
|
const formatFileSize = (bytes: number): string => {
|
|
168
132
|
if (bytes < 1024) return bytes + ' B'
|
|
169
|
-
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(
|
|
170
|
-
return (bytes / (1024 * 1024)).toFixed(
|
|
133
|
+
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + ' KB'
|
|
134
|
+
return (bytes / (1024 * 1024)).toFixed(2) + ' MB'
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (error) {
|
|
138
|
+
return (
|
|
139
|
+
<div className="text-center text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed">
|
|
140
|
+
<p className="text-red-500">{error}</p>
|
|
141
|
+
</div>
|
|
142
|
+
)
|
|
171
143
|
}
|
|
172
144
|
|
|
173
145
|
return (
|
|
174
|
-
<
|
|
175
|
-
|
|
176
|
-
<div className="
|
|
177
|
-
<
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
146
|
+
<div>
|
|
147
|
+
{exportedFiles.length > 0 && (
|
|
148
|
+
<div className="space-y-2">
|
|
149
|
+
<div className="border rounded-md divide-y">
|
|
150
|
+
{paginatedFiles.map((file) => (
|
|
151
|
+
<div key={file.fileName} className="p-4 hover:bg-muted/50 transition-colors">
|
|
152
|
+
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
|
|
153
|
+
<div className="flex-1">
|
|
154
|
+
<p className="font-medium">{file.fileName}</p>
|
|
155
|
+
<div className="flex flex-col sm:flex-row gap-2 sm:gap-4 text-sm text-muted-foreground mt-1">
|
|
156
|
+
<span>Size: {formatFileSize(file.size)}</span>
|
|
157
|
+
<span>
|
|
158
|
+
Created:{' '}
|
|
159
|
+
{new Date(file.created).toLocaleString(navigator.language || undefined)}
|
|
160
|
+
</span>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
<div className="flex items-center gap-2">
|
|
164
|
+
<Button variant="outline" size="sm" onClick={() => downLoadFile(file.fileName)}>
|
|
165
|
+
<DownloadIcon className="mr-2 h-4 w-4" />
|
|
166
|
+
Download
|
|
167
|
+
</Button>
|
|
168
|
+
<Button
|
|
169
|
+
variant="outline"
|
|
170
|
+
size="sm"
|
|
171
|
+
onClick={() => handleDeleteClick(file.fileName)}
|
|
172
|
+
className="text-destructive hover:text-destructive"
|
|
173
|
+
>
|
|
174
|
+
<TrashIcon className="h-4 w-4" /> Delete
|
|
175
|
+
</Button>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
))}
|
|
200
180
|
</div>
|
|
201
|
-
) : (
|
|
202
|
-
<>
|
|
203
|
-
<div className="rounded-md border overflow-hidden">
|
|
204
|
-
<Table>
|
|
205
|
-
<TableHeader>
|
|
206
|
-
<TableRow>
|
|
207
|
-
<TableHead className="w-[140px]">Type</TableHead>
|
|
208
|
-
<TableHead>File</TableHead>
|
|
209
|
-
<TableHead>Created</TableHead>
|
|
210
|
-
<TableHead className="text-right">Actions</TableHead>
|
|
211
|
-
</TableRow>
|
|
212
|
-
</TableHeader>
|
|
213
|
-
<TableBody>
|
|
214
|
-
{paginatedFiles.length === 0 ? (
|
|
215
|
-
<TableRow>
|
|
216
|
-
<TableCell colSpan={4} className="text-center text-muted-foreground py-8">
|
|
217
|
-
No results match your filters.
|
|
218
|
-
</TableCell>
|
|
219
|
-
</TableRow>
|
|
220
|
-
) : (
|
|
221
|
-
paginatedFiles.map((file) => (
|
|
222
|
-
<TableRow key={file.fileName}>
|
|
223
|
-
<TableCell>
|
|
224
|
-
<div className="flex items-center gap-2">
|
|
225
|
-
<span className="bg-primary/10 text-primary flex h-8 w-8 shrink-0 items-center justify-center rounded-md">
|
|
226
|
-
<DownloadIcon className="h-4 w-4" aria-hidden />
|
|
227
|
-
</span>
|
|
228
|
-
<span className="text-sm font-medium">Export</span>
|
|
229
|
-
</div>
|
|
230
|
-
</TableCell>
|
|
231
|
-
<TableCell>
|
|
232
|
-
<div className="font-medium truncate max-w-xs">{file.fileName}</div>
|
|
233
|
-
<div className="text-xs text-muted-foreground">
|
|
234
|
-
{formatFileSize(file.size)}
|
|
235
|
-
</div>
|
|
236
|
-
</TableCell>
|
|
237
|
-
<TableCell>
|
|
238
|
-
<span className="text-sm">
|
|
239
|
-
{new Date(file.created).toLocaleString(navigator.language || undefined)}
|
|
240
|
-
</span>
|
|
241
|
-
</TableCell>
|
|
242
|
-
<TableCell className="text-right">
|
|
243
|
-
<div className="inline-flex gap-2">
|
|
244
|
-
<Button
|
|
245
|
-
variant="outline"
|
|
246
|
-
size="sm"
|
|
247
|
-
onClick={() => downloadFile(file.fileName)}
|
|
248
|
-
>
|
|
249
|
-
<DownloadIcon className="mr-1 h-3.5 w-3.5" /> Download
|
|
250
|
-
</Button>
|
|
251
|
-
<Button
|
|
252
|
-
variant="ghost"
|
|
253
|
-
size="sm"
|
|
254
|
-
onClick={() => handleDeleteClick(file.fileName)}
|
|
255
|
-
className="text-muted-foreground hover:text-destructive"
|
|
256
|
-
>
|
|
257
|
-
<TrashIcon className="h-3.5 w-3.5" />
|
|
258
|
-
</Button>
|
|
259
|
-
</div>
|
|
260
|
-
</TableCell>
|
|
261
|
-
</TableRow>
|
|
262
|
-
))
|
|
263
|
-
)}
|
|
264
|
-
</TableBody>
|
|
265
|
-
</Table>
|
|
266
|
-
</div>
|
|
267
181
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
<Button
|
|
288
|
-
variant="outline"
|
|
289
|
-
size="sm"
|
|
290
|
-
onClick={() => setCurrentPage((prev) => Math.min(totalPages, prev + 1))}
|
|
291
|
-
disabled={currentPage === totalPages}
|
|
292
|
-
>
|
|
293
|
-
<ChevronRightIcon className="h-4 w-4" />
|
|
294
|
-
</Button>
|
|
182
|
+
{/* Pagination controls */}
|
|
183
|
+
{totalPages > 1 && (
|
|
184
|
+
<div className="flex items-center justify-between py-2 border-t">
|
|
185
|
+
<div className="text-sm text-muted-foreground">
|
|
186
|
+
Showing {(currentPage - 1) * itemsPerPage + 1} to{' '}
|
|
187
|
+
{Math.min(currentPage * itemsPerPage, exportedFiles.length)} of{' '}
|
|
188
|
+
{exportedFiles.length}
|
|
189
|
+
</div>
|
|
190
|
+
<div className="flex items-center gap-1">
|
|
191
|
+
<Button
|
|
192
|
+
variant="outline"
|
|
193
|
+
size="sm"
|
|
194
|
+
onClick={() => setCurrentPage((prev) => Math.max(1, prev - 1))}
|
|
195
|
+
disabled={currentPage === 1}
|
|
196
|
+
>
|
|
197
|
+
<ChevronLeftIcon className="h-4 w-4" />
|
|
198
|
+
</Button>
|
|
199
|
+
<div className="text-sm text-muted-foreground px-2">
|
|
200
|
+
{currentPage} / {totalPages}
|
|
295
201
|
</div>
|
|
202
|
+
<Button
|
|
203
|
+
variant="outline"
|
|
204
|
+
size="sm"
|
|
205
|
+
onClick={() => setCurrentPage((prev) => Math.min(totalPages, prev + 1))}
|
|
206
|
+
disabled={currentPage === totalPages}
|
|
207
|
+
>
|
|
208
|
+
<ChevronRightIcon className="h-4 w-4" />
|
|
209
|
+
</Button>
|
|
296
210
|
</div>
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
211
|
+
</div>
|
|
212
|
+
)}
|
|
213
|
+
</div>
|
|
214
|
+
)}
|
|
215
|
+
|
|
216
|
+
{exportedFiles.length === 0 && (
|
|
217
|
+
<div className="text-center py-8 text-muted-foreground text-sm">
|
|
218
|
+
<p>You have no exported files</p>
|
|
219
|
+
</div>
|
|
220
|
+
)}
|
|
301
221
|
|
|
302
222
|
<Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
|
|
303
223
|
<DialogContent>
|
|
@@ -317,7 +237,7 @@ export function ExportedList() {
|
|
|
317
237
|
</DialogFooter>
|
|
318
238
|
</DialogContent>
|
|
319
239
|
</Dialog>
|
|
320
|
-
</
|
|
240
|
+
</div>
|
|
321
241
|
)
|
|
322
242
|
}
|
|
323
243
|
|