@haus-tech/product-import-export-plugin 3.0.22 → 3.0.25-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 +47 -0
- package/README.md +90 -0
- package/package.json +8 -14
- package/{dist → src}/api/plugin.controller.js +3 -2
- package/src/api/plugin.controller.js.map +1 -0
- package/{dist → src}/api/product-export.controller.d.ts +1 -1
- package/{dist → src}/api/product-export.controller.js +28 -13
- package/src/api/product-export.controller.js.map +1 -0
- package/{dist → src}/api/product-import.controller.d.ts +0 -1
- package/{dist → src}/api/product-import.controller.js +3 -2
- package/src/api/product-import.controller.js.map +1 -0
- package/{dist → src}/constants.js +1 -0
- package/src/constants.js.map +1 -0
- package/{dist → src}/helpers/generate-asset-hash.js +2 -2
- package/src/helpers/generate-asset-hash.js.map +1 -0
- package/{dist → src}/helpers/normalize-string.js +2 -2
- package/src/helpers/normalize-string.js.map +1 -0
- package/{dist → src}/helpers/remove-update-at-fields.js +2 -2
- package/src/helpers/remove-update-at-fields.js.map +1 -0
- package/{dist → src}/index.js +1 -0
- package/src/index.js.map +1 -0
- package/{dist → src}/product-import-export.plugin.js +23 -11
- package/src/product-import-export.plugin.js.map +1 -0
- package/{dist → src}/providers/import-providers/asset-importer.d.ts +0 -1
- package/{dist → src}/providers/import-providers/asset-importer.js +3 -2
- package/src/providers/import-providers/asset-importer.js.map +1 -0
- package/{dist → src}/providers/import-providers/import-parser.d.ts +0 -1
- package/{dist → src}/providers/import-providers/import-parser.js +3 -2
- package/src/providers/import-providers/import-parser.js.map +1 -0
- package/{dist → src}/providers/import-providers/product-importer.d.ts +0 -1
- package/{dist → src}/providers/import-providers/product-importer.js +5 -4
- package/src/providers/import-providers/product-importer.js.map +1 -0
- package/{dist → src}/services/extended-fast-importer.service.js +3 -2
- package/src/services/extended-fast-importer.service.js.map +1 -0
- package/{dist → src}/services/product-export.service.js +28 -18
- package/src/services/product-export.service.js.map +1 -0
- package/{dist → src}/services/product-import.service.d.ts +0 -1
- package/{dist → src}/services/product-import.service.js +3 -2
- package/src/services/product-import.service.js.map +1 -0
- package/{dist → src}/types.js +1 -0
- package/src/types.js.map +1 -0
- package/dist/ui/components/export-dialog.component.d.ts +0 -45
- package/dist/ui/components/export-dialog.component.js +0 -126
- package/dist/ui/components/product-import.component.d.ts +0 -57
- package/dist/ui/components/product-import.component.js +0 -413
- package/dist/ui/product-export.service.d.ts +0 -14
- package/dist/ui/product-export.service.js +0 -107
- package/dist/ui/providers.d.ts +0 -2
- package/dist/ui/providers.js +0 -49
- package/dist/ui/routes.d.ts +0 -31
- package/dist/ui/routes.js +0 -12
- /package/{dist → src}/api/plugin.controller.d.ts +0 -0
- /package/{dist → src}/constants.d.ts +0 -0
- /package/{dist → src}/helpers/generate-asset-hash.d.ts +0 -0
- /package/{dist → src}/helpers/normalize-string.d.ts +0 -0
- /package/{dist → src}/helpers/remove-update-at-fields.d.ts +0 -0
- /package/{dist → src}/index.d.ts +0 -0
- /package/{dist → src}/product-import-export.plugin.d.ts +0 -0
- /package/{dist → src}/services/extended-fast-importer.service.d.ts +0 -0
- /package/{dist → src}/services/product-export.service.d.ts +0 -0
- /package/{dist → src}/types.d.ts +0 -0
- /package/{dist/ui → ui}/components/export-dialog.component.html +0 -0
- /package/{dist/ui → ui}/components/export-dialog.component.ts +0 -0
- /package/{dist/ui → ui}/components/product-import.component.html +0 -0
- /package/{dist/ui → ui}/components/product-import.component.ts +0 -0
- /package/{dist/ui → ui}/product-export.service.ts +0 -0
- /package/{dist/ui → ui}/providers.ts +0 -0
- /package/{dist/ui → ui}/routes.ts +0 -0
- /package/{dist/ui → ui}/translations/en.json +0 -0
- /package/{dist/ui → ui}/translations/sv.json +0 -0
|
@@ -1,413 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
-
};
|
|
8
|
-
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
-
};
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.ProductImportComponent = void 0;
|
|
13
|
-
const core_1 = require("@vendure/admin-ui/core");
|
|
14
|
-
const core_2 = require("@angular/core");
|
|
15
|
-
const core_3 = require("@vendure/admin-ui/core");
|
|
16
|
-
const core_4 = require("@ngx-translate/core");
|
|
17
|
-
const lodash_1 = require("lodash");
|
|
18
|
-
let ProductImportComponent = class ProductImportComponent {
|
|
19
|
-
notificationService;
|
|
20
|
-
cd;
|
|
21
|
-
translate;
|
|
22
|
-
localStorageService;
|
|
23
|
-
serverPath;
|
|
24
|
-
validateFile = null;
|
|
25
|
-
selectedFile = null;
|
|
26
|
-
updateProductSlug = true;
|
|
27
|
-
selectedMainLanguage;
|
|
28
|
-
availableLanguages = [];
|
|
29
|
-
updatingStrategy = 'merge';
|
|
30
|
-
fileUploadElement;
|
|
31
|
-
config;
|
|
32
|
-
appConfig;
|
|
33
|
-
currentChannel;
|
|
34
|
-
constructor(notificationService, cd, translate, localStorageService) {
|
|
35
|
-
this.notificationService = notificationService;
|
|
36
|
-
this.cd = cd;
|
|
37
|
-
this.translate = translate;
|
|
38
|
-
this.localStorageService = localStorageService;
|
|
39
|
-
this.serverPath = (0, core_1.getServerLocation)();
|
|
40
|
-
}
|
|
41
|
-
async ngOnInit() {
|
|
42
|
-
this.appConfig = await fetch('./vendure-ui-config.json').then((res) => res.json());
|
|
43
|
-
this.config = await fetch(`${this.serverPath}/product-import-export/config`, {
|
|
44
|
-
method: 'GET',
|
|
45
|
-
headers: {
|
|
46
|
-
'Content-Type': 'application/json',
|
|
47
|
-
},
|
|
48
|
-
})
|
|
49
|
-
.then((res) => res.json())
|
|
50
|
-
.then((data) => data.importOptions);
|
|
51
|
-
}
|
|
52
|
-
doValidation(file) {
|
|
53
|
-
this.validateCsvStructure(file).then(({ isValid, langCodes, clearFile = true }) => {
|
|
54
|
-
if (isValid) {
|
|
55
|
-
this.selectedFile = file;
|
|
56
|
-
}
|
|
57
|
-
else if (clearFile) {
|
|
58
|
-
this.clearFile();
|
|
59
|
-
}
|
|
60
|
-
if (langCodes && !this.selectedMainLanguage) {
|
|
61
|
-
this.availableLanguages = langCodes.filter((code) => code);
|
|
62
|
-
this.selectedMainLanguage =
|
|
63
|
-
(0, lodash_1.size)(this.availableLanguages) === 1
|
|
64
|
-
? this.availableLanguages?.[0]
|
|
65
|
-
: undefined;
|
|
66
|
-
}
|
|
67
|
-
this.cd.detectChanges();
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
onMainLanguageChange() {
|
|
71
|
-
this.cd.detectChanges();
|
|
72
|
-
if (this.validateFile) {
|
|
73
|
-
this.doValidation(this.validateFile);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
onFileSelected(event) {
|
|
77
|
-
const input = event.target;
|
|
78
|
-
if (input.files && input.files.length > 0) {
|
|
79
|
-
const file = input.files[0];
|
|
80
|
-
if (file.type !== 'text/csv') {
|
|
81
|
-
this.notificationService.error(this.translate.instant('product-import.notifications.invalid-file-type'));
|
|
82
|
-
this.clearFile();
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
this.validateFile = file;
|
|
86
|
-
this.doValidation(file);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
clearFile() {
|
|
90
|
-
this.fileUploadElement.nativeElement.value = '';
|
|
91
|
-
this.selectedFile = null;
|
|
92
|
-
this.validateFile = null;
|
|
93
|
-
this.selectedMainLanguage = undefined;
|
|
94
|
-
this.updatingStrategy = 'merge';
|
|
95
|
-
}
|
|
96
|
-
async validateCsvStructure(file) {
|
|
97
|
-
const baseColumns = ['sku'];
|
|
98
|
-
const optionalColumns = [
|
|
99
|
-
'assets',
|
|
100
|
-
'price',
|
|
101
|
-
'taxCategory',
|
|
102
|
-
'stockOnHand',
|
|
103
|
-
'trackInventory',
|
|
104
|
-
'variantAssets',
|
|
105
|
-
];
|
|
106
|
-
const translatableBaseColumns = ['name'];
|
|
107
|
-
const translatableColumns = [
|
|
108
|
-
'slug',
|
|
109
|
-
'description',
|
|
110
|
-
'facets',
|
|
111
|
-
'optionGroups',
|
|
112
|
-
'optionValues',
|
|
113
|
-
'variantFacets',
|
|
114
|
-
];
|
|
115
|
-
return new Promise((resolve) => {
|
|
116
|
-
const reader = new FileReader();
|
|
117
|
-
reader.onload = async (e) => {
|
|
118
|
-
const text = e.target?.result;
|
|
119
|
-
const lines = text.split('\n');
|
|
120
|
-
// Remove empty lines
|
|
121
|
-
lines.forEach((line, index) => {
|
|
122
|
-
if (line.trim() === '') {
|
|
123
|
-
lines.splice(index, 1);
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
// Check if number of columns for each line is consistent and the same as headers
|
|
127
|
-
const headerLength = this.splitLines(lines[0]).length;
|
|
128
|
-
const inconsistentLines = lines.filter((line) => this.splitLines(line).length !== headerLength);
|
|
129
|
-
if (inconsistentLines.length > 0) {
|
|
130
|
-
console.log('Inconsistent number of columns in the CSV file.');
|
|
131
|
-
this.notificationService.error(this.translate.instant('product-import.notifications.inconsistent-columns'));
|
|
132
|
-
resolve({ isValid: false });
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
if (lines.length > 0) {
|
|
136
|
-
const header = this.splitLines(lines[0]).map((col) => col.trim());
|
|
137
|
-
console.log('CSV Header:', header);
|
|
138
|
-
this.currentChannel = await fetch(`${this.serverPath}/product-import-export/channel`, {
|
|
139
|
-
method: 'GET',
|
|
140
|
-
headers: {
|
|
141
|
-
'Content-Type': 'application/json',
|
|
142
|
-
...this.getRequestHeaders(),
|
|
143
|
-
},
|
|
144
|
-
})
|
|
145
|
-
.then((res) => res.json())
|
|
146
|
-
.then((data) => data);
|
|
147
|
-
const availableLanguages = this.currentChannel.availableLanguageCodes;
|
|
148
|
-
// Extract all language codes from headers
|
|
149
|
-
const languageCodes = header
|
|
150
|
-
.map((col) => col.match(/:(\w{2})$/)?.[1])
|
|
151
|
-
.filter((code, index, self) => code && self.indexOf(code) === index);
|
|
152
|
-
console.log('Language Codes:', languageCodes);
|
|
153
|
-
let validationLanguage = (0, lodash_1.size)(languageCodes) === 0 ? undefined : this.selectedMainLanguage;
|
|
154
|
-
if (!this.selectedMainLanguage && (0, lodash_1.size)(languageCodes) < 2) {
|
|
155
|
-
validationLanguage = languageCodes?.[0];
|
|
156
|
-
}
|
|
157
|
-
if (!this.selectedMainLanguage && (0, lodash_1.size)(languageCodes) > 1) {
|
|
158
|
-
console.log('Multiple languages found in the CSV file. Please select the main language.');
|
|
159
|
-
resolve({ isValid: true, langCodes: languageCodes, clearFile: false });
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
if (!this.selectedMainLanguage && (0, lodash_1.size)(languageCodes) === 0) {
|
|
163
|
-
resolve({ isValid: true, langCodes: availableLanguages, clearFile: false });
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
console.log('Validation language:', validationLanguage);
|
|
167
|
-
// Check if csv-file includes languages that are not available in the system
|
|
168
|
-
const missingLanguages = languageCodes.filter((code) => !availableLanguages.includes(code));
|
|
169
|
-
if (missingLanguages.length > 0) {
|
|
170
|
-
const missingLangs = missingLanguages.join(', ');
|
|
171
|
-
console.log('Missing languages:', missingLangs);
|
|
172
|
-
this.notificationService.error(`${this.translate.instant('product-import.notifications.missing-languages')} ${missingLangs}`);
|
|
173
|
-
resolve({ isValid: false });
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
// Validate base columns
|
|
177
|
-
const missingBaseColumns = baseColumns.filter((col) => !header.includes(col));
|
|
178
|
-
const missingTranslatableBaseColumns = translatableBaseColumns.filter((col) => !header.includes(validationLanguage ? `${col}:${validationLanguage}` : col));
|
|
179
|
-
const combinedMissingBaseColumns = [
|
|
180
|
-
...missingBaseColumns,
|
|
181
|
-
...missingTranslatableBaseColumns,
|
|
182
|
-
];
|
|
183
|
-
if (combinedMissingBaseColumns.length > 0) {
|
|
184
|
-
const missingColumns = combinedMissingBaseColumns.join(', ');
|
|
185
|
-
console.log('Missing base columns:', missingColumns);
|
|
186
|
-
this.notificationService.error(`${this.translate.instant('product-import.notifications.missing-base-columns')} ${missingColumns}`);
|
|
187
|
-
resolve({ isValid: false });
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
const nameColumn = `name:${validationLanguage}` || 'name';
|
|
191
|
-
// Get optionGroups and optionValues columns for the main language
|
|
192
|
-
const optionGroupsColumn = `optionGroups:${validationLanguage}` || 'optionGroups';
|
|
193
|
-
const optionValuesColumn = `optionValues:${validationLanguage}` || 'optionValues';
|
|
194
|
-
const optionGroupsIndex = header.indexOf(optionGroupsColumn);
|
|
195
|
-
const optionValuesIndex = header.indexOf(optionValuesColumn);
|
|
196
|
-
// if (optionGroupsIndex === -1 || optionValuesIndex === -1) {
|
|
197
|
-
// console.log(
|
|
198
|
-
// `Missing optionGroups or optionValues columns for language: ${validationLanguage}`,
|
|
199
|
-
// )
|
|
200
|
-
// this.notificationService.error(
|
|
201
|
-
// `${this.translate.instant(
|
|
202
|
-
// 'product-import.notifications.missing-option-columns',
|
|
203
|
-
// )} ${validationLanguage}`,
|
|
204
|
-
// )
|
|
205
|
-
// resolve({ isValid: false })
|
|
206
|
-
// return
|
|
207
|
-
// }
|
|
208
|
-
// Loop through each row to validate the option values
|
|
209
|
-
let currentOptionGroupCount = 0;
|
|
210
|
-
for (let i = 1; i < lines.length; i++) {
|
|
211
|
-
const row = this.splitLines(lines[i]).map((col) => col.trim());
|
|
212
|
-
// Check if it's a product row (it has a 'name' field)
|
|
213
|
-
const isProductRow = row[header.indexOf(nameColumn)];
|
|
214
|
-
if (isProductRow) {
|
|
215
|
-
// It's a new product row, get the option groups
|
|
216
|
-
const optionGroups = (row[optionGroupsIndex]?.split('|') ?? []).filter((group) => group !== '');
|
|
217
|
-
const optionValues = (row[optionValuesIndex]?.split('|') ?? []).filter((value) => value !== '');
|
|
218
|
-
currentOptionGroupCount = optionGroups.length;
|
|
219
|
-
// Validate that the number of option values matches the number of option groups
|
|
220
|
-
if (optionValues.length !== currentOptionGroupCount) {
|
|
221
|
-
this.notificationService.error(`${this.translate.instant('product-import.notifications.mismatched-option-values')} Row: ${i + 1}. Expected ${currentOptionGroupCount} option values but got ${optionValues.length}.`);
|
|
222
|
-
resolve({ isValid: false });
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
else {
|
|
227
|
-
// It's a variant row, validate the option values
|
|
228
|
-
const optionValues = (row[optionValuesIndex]?.split('|') ?? []).filter((value) => value !== '');
|
|
229
|
-
// Validate that the number of option values matches the product's option groups
|
|
230
|
-
if (optionValues.length !== currentOptionGroupCount) {
|
|
231
|
-
this.notificationService.error(`${this.translate.instant('product-import.notifications.mismatched-option-values')} Row: ${i + 1}. Expected ${currentOptionGroupCount} option values but got ${optionValues.length}. ${JSON.stringify(optionValues)}`);
|
|
232
|
-
resolve({ isValid: false });
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
console.log('All option values for the main language are valid.');
|
|
238
|
-
// Check if a row contains JSON, if so check if it is valid. This should be done for all columns, not just translatable ones.
|
|
239
|
-
for (let i = 1; i < lines.length; i++) {
|
|
240
|
-
const row = this.splitLines(lines[i]).map((col) => col.trim());
|
|
241
|
-
for (const col of header) {
|
|
242
|
-
const colIndex = header.indexOf(col);
|
|
243
|
-
if (colIndex !== -1 && row[colIndex]) {
|
|
244
|
-
try {
|
|
245
|
-
const { isJson } = this.checkifJson(row[colIndex]);
|
|
246
|
-
if (isJson) {
|
|
247
|
-
JSON.parse(row[colIndex]
|
|
248
|
-
.trim()
|
|
249
|
-
.replace(/^"/, '')
|
|
250
|
-
.replace(/"$/, '')
|
|
251
|
-
.replace(/'/g, '"')
|
|
252
|
-
.replace(/\s+/g, ' ')
|
|
253
|
-
.replace(/,\s*'/g, ", '"));
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
catch (error) {
|
|
257
|
-
console.log(`Invalid JSON in column: ${col}, row: ${i + 1}`);
|
|
258
|
-
this.notificationService.error(this.translate.instant('product-import.notifications.invalid-json', {
|
|
259
|
-
col,
|
|
260
|
-
row: i + 1,
|
|
261
|
-
}));
|
|
262
|
-
resolve({ isValid: false });
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
console.log('All JSON values are valid.');
|
|
269
|
-
const { errorRows: missingBaseTranslatableValuesRows } = this.checkTranslatabeColumns(lines, translatableBaseColumns, header, languageCodes);
|
|
270
|
-
if (missingBaseTranslatableValuesRows.length > 0) {
|
|
271
|
-
console.log('Missing translation values in rows:', missingBaseTranslatableValuesRows);
|
|
272
|
-
this.notificationService.error(`${this.translate.instant('product-import.notifications.missing-main-language-name')} Rows: ${(0, lodash_1.uniq)(missingBaseTranslatableValuesRows).join(', ')}`);
|
|
273
|
-
resolve({ isValid: false });
|
|
274
|
-
return;
|
|
275
|
-
}
|
|
276
|
-
// Check if all translatable columns have values for the main language
|
|
277
|
-
const { errorRows, errorColumns } = this.checkTranslatabeColumns(lines, translatableColumns, header, languageCodes);
|
|
278
|
-
if (errorRows.length > 0) {
|
|
279
|
-
console.log('Missing translation values in rows:', errorRows);
|
|
280
|
-
this.notificationService.warning(`${this.translate.instant('product-import.notifications.missing-translation-values')} Rows: ${(0, lodash_1.uniq)(errorRows).join(', ')} Columns: ${(0, lodash_1.uniq)(errorColumns).join(', ')}`);
|
|
281
|
-
}
|
|
282
|
-
resolve({ isValid: true, langCodes: languageCodes });
|
|
283
|
-
}
|
|
284
|
-
else {
|
|
285
|
-
console.log('No lines in CSV file.');
|
|
286
|
-
this.notificationService.error(this.translate.instant('product-import.notifications.no-lines-in-file'));
|
|
287
|
-
resolve({ isValid: false });
|
|
288
|
-
}
|
|
289
|
-
};
|
|
290
|
-
reader.onerror = () => {
|
|
291
|
-
console.log('Error reading file.');
|
|
292
|
-
this.notificationService.error(this.translate.instant('product-import.notifications.file-read-error'));
|
|
293
|
-
resolve({ isValid: false });
|
|
294
|
-
};
|
|
295
|
-
reader.readAsText(file);
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
async uploadFile() {
|
|
299
|
-
if (this.selectedFile && this.selectedMainLanguage && this.updatingStrategy) {
|
|
300
|
-
const formData = new FormData();
|
|
301
|
-
formData.append('file', this.selectedFile);
|
|
302
|
-
formData.append('updateProductSlug', this.updateProductSlug.toString());
|
|
303
|
-
formData.append('mainLanguage', this.selectedMainLanguage);
|
|
304
|
-
formData.append('updatingStrategy', this.updatingStrategy);
|
|
305
|
-
try {
|
|
306
|
-
const res = await fetch(`${this.serverPath}/product-import/upload`, {
|
|
307
|
-
method: 'POST',
|
|
308
|
-
body: formData,
|
|
309
|
-
headers: this.getRequestHeaders(),
|
|
310
|
-
});
|
|
311
|
-
if (res.ok) {
|
|
312
|
-
this.clearFile();
|
|
313
|
-
this.notificationService.success(this.translate.instant('product-import.notifications.upload-success'));
|
|
314
|
-
this.cd.detectChanges();
|
|
315
|
-
}
|
|
316
|
-
else {
|
|
317
|
-
this.notificationService.error(this.translate.instant('product-import.notifications.upload-error'));
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
catch (error) {
|
|
321
|
-
this.notificationService.error(this.translate.instant('product-import.notifications.upload-error'));
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
else {
|
|
325
|
-
this.notificationService.error(this.translate.instant('product-import.notifications.no-file-selected'));
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
splitLines(text) {
|
|
329
|
-
// Split text by commas, but not if comma is inside double quotes
|
|
330
|
-
const splittedText = text.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);
|
|
331
|
-
return splittedText;
|
|
332
|
-
}
|
|
333
|
-
checkifJson(text) {
|
|
334
|
-
// Replace starting and ending double quotes if exists
|
|
335
|
-
text = text.trim().replace(/^"/, '').replace(/"$/, '');
|
|
336
|
-
return {
|
|
337
|
-
startsWithJson: (0, lodash_1.startsWith)(text.trim(), '{') || (0, lodash_1.startsWith)(text.trim(), '['),
|
|
338
|
-
endsWithJson: (0, lodash_1.endsWith)(text.trim(), '}') || (0, lodash_1.endsWith)(text.trim(), ']'),
|
|
339
|
-
startChar: text.trim().charAt(0),
|
|
340
|
-
endChar: text.trim().charAt(text.trim().length - 1),
|
|
341
|
-
isJson: (0, lodash_1.startsWith)(text.trim(), '{') ||
|
|
342
|
-
(0, lodash_1.startsWith)(text.trim(), '[') ||
|
|
343
|
-
(0, lodash_1.endsWith)(text.trim(), '}') ||
|
|
344
|
-
(0, lodash_1.endsWith)(text.trim(), ']'),
|
|
345
|
-
};
|
|
346
|
-
}
|
|
347
|
-
getRequestHeaders() {
|
|
348
|
-
const headers = {};
|
|
349
|
-
const channelToken = this.localStorageService.get('activeChannelToken');
|
|
350
|
-
const { tokenMethod, channelTokenKey } = this.appConfig;
|
|
351
|
-
if (channelToken) {
|
|
352
|
-
headers[channelTokenKey ?? 'vendure-token'] = channelToken;
|
|
353
|
-
}
|
|
354
|
-
if (tokenMethod === 'bearer') {
|
|
355
|
-
const authToken = this.localStorageService.get('authToken');
|
|
356
|
-
if (authToken) {
|
|
357
|
-
headers.authorization = `Bearer ${authToken}`;
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
return headers;
|
|
361
|
-
}
|
|
362
|
-
checkTranslatabeColumns(lines, columns, header, languageCodes) {
|
|
363
|
-
let errorRows = [];
|
|
364
|
-
let errorColumns = [];
|
|
365
|
-
if ((0, lodash_1.size)(languageCodes) < 2) {
|
|
366
|
-
console.log('Only one language found in the file.');
|
|
367
|
-
return { errorRows, errorColumns };
|
|
368
|
-
}
|
|
369
|
-
for (let i = 1; i < lines.length; i++) {
|
|
370
|
-
const row = this.splitLines(lines[i]).map((col) => col.trim());
|
|
371
|
-
for (const baseColumn of columns) {
|
|
372
|
-
const mainLanguageColumn = `${baseColumn}:${this.selectedMainLanguage}`;
|
|
373
|
-
const colIndex = header.indexOf(mainLanguageColumn);
|
|
374
|
-
const mainLanguageValue = row[colIndex];
|
|
375
|
-
const mainLanguageHasValue = mainLanguageValue !== undefined && mainLanguageValue !== '';
|
|
376
|
-
if (mainLanguageHasValue) {
|
|
377
|
-
continue;
|
|
378
|
-
}
|
|
379
|
-
for (const lang of languageCodes) {
|
|
380
|
-
if (lang) {
|
|
381
|
-
// Check if lang is defined
|
|
382
|
-
const langSpecificColumn = `${baseColumn}:${lang}`;
|
|
383
|
-
const colIndex = header.indexOf(langSpecificColumn);
|
|
384
|
-
const colValue = row[colIndex];
|
|
385
|
-
const colHasValue = colValue !== undefined && colValue !== '';
|
|
386
|
-
if (!mainLanguageHasValue && colHasValue) {
|
|
387
|
-
errorRows.push(i + 1);
|
|
388
|
-
errorColumns.push(langSpecificColumn);
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
return { errorRows, errorColumns };
|
|
395
|
-
}
|
|
396
|
-
};
|
|
397
|
-
__decorate([
|
|
398
|
-
(0, core_2.ViewChild)('fileUpload', { static: false }),
|
|
399
|
-
__metadata("design:type", core_2.ElementRef)
|
|
400
|
-
], ProductImportComponent.prototype, "fileUploadElement", void 0);
|
|
401
|
-
ProductImportComponent = __decorate([
|
|
402
|
-
(0, core_2.Component)({
|
|
403
|
-
selector: 'product-import',
|
|
404
|
-
templateUrl: './product-import.component.html',
|
|
405
|
-
standalone: true,
|
|
406
|
-
imports: [core_1.SharedModule],
|
|
407
|
-
}),
|
|
408
|
-
__metadata("design:paramtypes", [core_3.NotificationService,
|
|
409
|
-
core_2.ChangeDetectorRef,
|
|
410
|
-
core_4.TranslateService,
|
|
411
|
-
core_3.LocalStorageService])
|
|
412
|
-
], ProductImportComponent);
|
|
413
|
-
exports.ProductImportComponent = ProductImportComponent;
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { DataService, LocalStorageService, NotificationService } from '@vendure/admin-ui/core';
|
|
2
|
-
import { Product } from '@vendure/core';
|
|
3
|
-
export declare class ProductExportService {
|
|
4
|
-
protected dataService: DataService;
|
|
5
|
-
private notificationService;
|
|
6
|
-
private localStorageService;
|
|
7
|
-
serverPath: string;
|
|
8
|
-
constructor(dataService: DataService, notificationService: NotificationService, localStorageService: LocalStorageService);
|
|
9
|
-
getCustomFields(productIds: string[]): Promise<string[]>;
|
|
10
|
-
getConfig(): Promise<any>;
|
|
11
|
-
exportProducts(selection: Product[], fileName?: string, customFields?: string[], exportAssetsAs?: 'url' | 'json', selectedExportFields?: string[]): Promise<void>;
|
|
12
|
-
private getHeaders;
|
|
13
|
-
private downloadBlob;
|
|
14
|
-
}
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
-
};
|
|
8
|
-
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
-
};
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.ProductExportService = void 0;
|
|
13
|
-
const core_1 = require("@angular/core");
|
|
14
|
-
const core_2 = require("@vendure/admin-ui/core");
|
|
15
|
-
let ProductExportService = class ProductExportService {
|
|
16
|
-
dataService;
|
|
17
|
-
notificationService;
|
|
18
|
-
localStorageService;
|
|
19
|
-
serverPath;
|
|
20
|
-
constructor(dataService, notificationService, localStorageService) {
|
|
21
|
-
this.dataService = dataService;
|
|
22
|
-
this.notificationService = notificationService;
|
|
23
|
-
this.localStorageService = localStorageService;
|
|
24
|
-
this.serverPath = (0, core_2.getServerLocation)();
|
|
25
|
-
}
|
|
26
|
-
async getCustomFields(productIds) {
|
|
27
|
-
return fetch(`${this.serverPath}/product-export/custom-fields`, {
|
|
28
|
-
method: 'POST',
|
|
29
|
-
headers: {
|
|
30
|
-
'Content-Type': 'application/json',
|
|
31
|
-
...this.getHeaders(),
|
|
32
|
-
},
|
|
33
|
-
body: JSON.stringify(productIds),
|
|
34
|
-
})
|
|
35
|
-
.then((res) => res.json())
|
|
36
|
-
.then((data) => {
|
|
37
|
-
return data.map((field) => field.name);
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
async getConfig() {
|
|
41
|
-
return fetch(`${this.serverPath}/product-import-export/config`, {
|
|
42
|
-
method: 'GET',
|
|
43
|
-
headers: {
|
|
44
|
-
'Content-Type': 'application/json',
|
|
45
|
-
...this.getHeaders(),
|
|
46
|
-
},
|
|
47
|
-
})
|
|
48
|
-
.then((res) => res.json())
|
|
49
|
-
.then((data) => data);
|
|
50
|
-
}
|
|
51
|
-
async exportProducts(selection, fileName, customFields, exportAssetsAs, selectedExportFields) {
|
|
52
|
-
this.notificationService.info(`Exporting ${selection.length} products to ${fileName}.csv`);
|
|
53
|
-
const productIds = selection.map((product) => product.id);
|
|
54
|
-
try {
|
|
55
|
-
const res = await fetch(`${this.serverPath}/product-export/export?fileName=${fileName}&customFields=${customFields}&exportAssetsAs=${exportAssetsAs}&selectedExportFields=${selectedExportFields}`, {
|
|
56
|
-
method: 'POST',
|
|
57
|
-
headers: {
|
|
58
|
-
'Content-Type': 'application/json',
|
|
59
|
-
...this.getHeaders(),
|
|
60
|
-
},
|
|
61
|
-
body: JSON.stringify(productIds),
|
|
62
|
-
});
|
|
63
|
-
if (!res.ok) {
|
|
64
|
-
const json = await res.json();
|
|
65
|
-
throw Error(json?.message);
|
|
66
|
-
}
|
|
67
|
-
const header = res.headers.get('Content-Disposition');
|
|
68
|
-
const parts = header.split(';');
|
|
69
|
-
const filename = parts[1].split('=')[1];
|
|
70
|
-
const blob = await res.blob();
|
|
71
|
-
await this.downloadBlob(blob, filename);
|
|
72
|
-
}
|
|
73
|
-
catch (err) {
|
|
74
|
-
console.error(err);
|
|
75
|
-
this.notificationService.error(err.message);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
getHeaders() {
|
|
79
|
-
const headers = {};
|
|
80
|
-
const channelToken = this.localStorageService.get('activeChannelToken');
|
|
81
|
-
if (channelToken) {
|
|
82
|
-
headers['vendure-token'] = channelToken;
|
|
83
|
-
}
|
|
84
|
-
const authToken = this.localStorageService.get('authToken');
|
|
85
|
-
if (authToken) {
|
|
86
|
-
headers.authorization = `Bearer ${authToken}`;
|
|
87
|
-
}
|
|
88
|
-
return headers;
|
|
89
|
-
}
|
|
90
|
-
async downloadBlob(blob, fileName) {
|
|
91
|
-
const blobUrl = window.URL.createObjectURL(blob);
|
|
92
|
-
const a = document.createElement('a');
|
|
93
|
-
document.body.appendChild(a);
|
|
94
|
-
a.setAttribute('hidden', 'true');
|
|
95
|
-
a.href = blobUrl;
|
|
96
|
-
a.download = fileName;
|
|
97
|
-
a.setAttribute('target', '_blank');
|
|
98
|
-
a.click();
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
ProductExportService = __decorate([
|
|
102
|
-
(0, core_1.Injectable)(),
|
|
103
|
-
__metadata("design:paramtypes", [core_2.DataService,
|
|
104
|
-
core_2.NotificationService,
|
|
105
|
-
core_2.LocalStorageService])
|
|
106
|
-
], ProductExportService);
|
|
107
|
-
exports.ProductExportService = ProductExportService;
|
package/dist/ui/providers.d.ts
DELETED
package/dist/ui/providers.js
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const core_1 = require("@vendure/admin-ui/core");
|
|
4
|
-
const product_import_component_1 = require("./components/product-import.component");
|
|
5
|
-
const export_dialog_component_1 = require("./components/export-dialog.component");
|
|
6
|
-
const core_2 = require("@vendure/admin-ui/core");
|
|
7
|
-
const product_export_service_1 = require("./product-export.service");
|
|
8
|
-
const ngx_translate_extract_marker_1 = require("@biesbjerg/ngx-translate-extract-marker");
|
|
9
|
-
exports.default = [
|
|
10
|
-
(0, core_1.registerPageTab)({
|
|
11
|
-
location: 'product-list',
|
|
12
|
-
tab: (0, ngx_translate_extract_marker_1.marker)('product-import.import-products'),
|
|
13
|
-
route: 'import',
|
|
14
|
-
tabIcon: 'import',
|
|
15
|
-
component: product_import_component_1.ProductImportComponent,
|
|
16
|
-
}),
|
|
17
|
-
(0, core_2.registerBulkAction)({
|
|
18
|
-
location: 'product-list',
|
|
19
|
-
label: (0, ngx_translate_extract_marker_1.marker)('product-export.action'),
|
|
20
|
-
icon: 'export',
|
|
21
|
-
onClick: ({ injector, selection }) => {
|
|
22
|
-
const modalService = injector.get(core_2.ModalService);
|
|
23
|
-
const productExportService = injector.get(product_export_service_1.ProductExportService);
|
|
24
|
-
const productIds = selection.map((product) => product.id);
|
|
25
|
-
const promises = [
|
|
26
|
-
productExportService.getCustomFields(productIds),
|
|
27
|
-
productExportService.getConfig(),
|
|
28
|
-
];
|
|
29
|
-
Promise.all(promises).then(([customFields, config]) => {
|
|
30
|
-
modalService
|
|
31
|
-
.fromComponent(export_dialog_component_1.ExportDialogComponent, {
|
|
32
|
-
size: 'md',
|
|
33
|
-
locals: {
|
|
34
|
-
selection,
|
|
35
|
-
customFields,
|
|
36
|
-
config: config.exportOptions,
|
|
37
|
-
},
|
|
38
|
-
closable: true,
|
|
39
|
-
})
|
|
40
|
-
.subscribe((response) => {
|
|
41
|
-
if (response?.result) {
|
|
42
|
-
productExportService.exportProducts(selection, response.fileName, response.selectedFields, response.exportAssetsAs, response.selectedExportFields);
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
},
|
|
47
|
-
}),
|
|
48
|
-
product_export_service_1.ProductExportService,
|
|
49
|
-
];
|
package/dist/ui/routes.d.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
declare const _default: {
|
|
2
|
-
resolve: {
|
|
3
|
-
detail?: import("@angular/router").ResolveFn<{
|
|
4
|
-
entity: import("rxjs").Observable<any>;
|
|
5
|
-
result?: any;
|
|
6
|
-
}> | undefined;
|
|
7
|
-
};
|
|
8
|
-
data: {
|
|
9
|
-
breadcrumb: import("rxjs").BehaviorSubject<import("@vendure/admin-ui/core").BreadcrumbValue> | ((data: any) => any);
|
|
10
|
-
locationId: string | undefined;
|
|
11
|
-
description: string | undefined;
|
|
12
|
-
};
|
|
13
|
-
component: typeof import("@vendure/admin-ui/core").AngularRouteComponent;
|
|
14
|
-
title?: string | import("@angular/core").Type<import("@angular/router").Resolve<string>> | import("@angular/router").ResolveFn<string> | undefined;
|
|
15
|
-
path: string;
|
|
16
|
-
pathMatch?: "prefix" | "full" | undefined;
|
|
17
|
-
matcher?: import("@angular/router").UrlMatcher | undefined;
|
|
18
|
-
loadComponent?: (() => import("@angular/core").Type<unknown> | import("rxjs").Observable<import("@angular/core").Type<unknown> | import("@angular/router").DefaultExport<import("@angular/core").Type<unknown>>> | Promise<import("@angular/core").Type<unknown> | import("@angular/router").DefaultExport<import("@angular/core").Type<unknown>>>) | undefined;
|
|
19
|
-
redirectTo?: string | undefined;
|
|
20
|
-
outlet?: string | undefined;
|
|
21
|
-
canActivate?: any[] | undefined;
|
|
22
|
-
canMatch?: any[] | undefined;
|
|
23
|
-
canActivateChild?: any[] | undefined;
|
|
24
|
-
canDeactivate?: any[] | undefined;
|
|
25
|
-
canLoad?: any[] | undefined;
|
|
26
|
-
children?: import("@angular/router").Routes | undefined;
|
|
27
|
-
loadChildren?: import("@angular/router").LoadChildrenCallback | undefined;
|
|
28
|
-
runGuardsAndResolvers?: import("@angular/router").RunGuardsAndResolvers | undefined;
|
|
29
|
-
providers: (import("@angular/core").Provider | import("@angular/core").EnvironmentProviders)[];
|
|
30
|
-
}[];
|
|
31
|
-
export default _default;
|
package/dist/ui/routes.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const core_1 = require("@vendure/admin-ui/core");
|
|
4
|
-
const product_import_component_1 = require("./components/product-import.component");
|
|
5
|
-
exports.default = [
|
|
6
|
-
(0, core_1.registerRouteComponent)({
|
|
7
|
-
component: product_import_component_1.ProductImportComponent,
|
|
8
|
-
path: '',
|
|
9
|
-
title: 'Import Products',
|
|
10
|
-
breadcrumb: 'Import Products',
|
|
11
|
-
}),
|
|
12
|
-
];
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/{dist → src}/index.d.ts
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/{dist → src}/types.d.ts
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|