@nkhang1902/strapi-plugin-export-import-clsx 1.1.153 → 1.2.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.
@@ -1,8 +1,5 @@
1
1
  import { useState, useRef } from "react";
2
2
  import {
3
- Modal,
4
- Typography,
5
- Box,
6
3
  Button,
7
4
  } from "@strapi/design-system";
8
5
  import { Download, Upload } from "@strapi/icons";
@@ -11,9 +8,6 @@ import { useNotification } from "@strapi/strapi/admin";
11
8
  const ExportImportButtons = (props) => {
12
9
  const [isExporting, setIsExporting] = useState(false);
13
10
  const [isImporting, setIsImporting] = useState(false);
14
- const [showErrorModal, setShowErrorModal] = useState(false);
15
- const [importErrors, setImportErrors] = useState([]);
16
-
17
11
  const { toggleNotification } = useNotification();
18
12
 
19
13
  // Get current content type from props or URL
@@ -105,26 +99,44 @@ const ExportImportButtons = (props) => {
105
99
  title,
106
100
  message,
107
101
  type = 'warning',
108
- duration = 15000, // 15 seconds
102
+ duration = 15000,
103
+ maxLines = 8,
109
104
  }) {
105
+ const lines = String(message).split('\n');
106
+
107
+ let displayText = message;
108
+ let extraCount = 0;
109
+ if (lines.length > maxLines) {
110
+ extraCount = lines.length - maxLines;
111
+ displayText =
112
+ lines.slice(0, maxLines).join('\n') +
113
+ `\n... (+${extraCount} more errors)`;
114
+ }
115
+
116
+ displayText = '\n' + displayText;
117
+
110
118
  toggle({
111
119
  type,
112
120
  title,
113
121
  message: (
114
- <div style={{
115
- maxHeight: '260px',
116
- overflowY: 'auto',
117
- whiteSpace: 'pre-wrap',
118
- fontSize: 13,
119
- lineHeight: 1.5,
120
- paddingRight: 4,
121
- }}>
122
- {message}
122
+ <div
123
+ style={{
124
+ maxHeight: 260,
125
+ overflowY: 'auto',
126
+ whiteSpace: 'pre-wrap',
127
+ fontSize: 13,
128
+ lineHeight: 1.5,
129
+ paddingTop: 4,
130
+ paddingRight: 4,
131
+ }}
132
+ >
133
+ {displayText}
123
134
  </div>
124
135
  ),
125
136
  timeout: duration,
126
137
  });
127
138
  }
139
+ const currentContentType = getContentType();
128
140
 
129
141
  const handleExport = async () => {
130
142
  const contentType = getContentType();
@@ -210,7 +222,7 @@ const ExportImportButtons = (props) => {
210
222
 
211
223
  const eventFilter = getEventFilter();
212
224
  try {
213
- const response = await fetch(`/export-import-clsx/import?eventId=${eventFilter.eventId}`, {
225
+ const response = await fetch(`/export-import-clsx/import?eventId=${eventFilter?.eventId || ""}`, {
214
226
  method: "POST",
215
227
  body: formData,
216
228
  });
@@ -224,8 +236,6 @@ const ExportImportButtons = (props) => {
224
236
  const total = created + updated;
225
237
 
226
238
  if (errorList.length > 0) {
227
- setImportErrors(errorList);
228
- setShowErrorModal(true);
229
239
  showLongNotification(toggleNotification, {
230
240
  title: "Import errors",
231
241
  message: errorList.join("\n"),
@@ -265,8 +275,10 @@ const ExportImportButtons = (props) => {
265
275
  // Create ref for file input
266
276
  const fileInputRef = useRef(null);
267
277
 
278
+ if (["api::event.event", "api::event-content.event-content", "api::meeting-participation-status.meeting-participation-status"].includes(currentContentType)) {
279
+ return null
280
+ }
268
281
  return (
269
- <>
270
282
  <div
271
283
  style={{
272
284
  display: "flex",
@@ -304,41 +316,6 @@ const ExportImportButtons = (props) => {
304
316
  Import
305
317
  </Button>
306
318
  </div>
307
- {showErrorModal && (
308
- <Modal.Root
309
- onClose={() => setShowErrorModal(false)}
310
- labelledBy="import-errors-title"
311
- >
312
- <Modal.Content>
313
- <Modal.Header>
314
- <Typography id="import-errors-title" fontWeight="bold">
315
- Import Errors ({importErrors.length})
316
- </Typography>
317
- </Modal.Header>
318
-
319
- <Modal.Body>
320
- <Box padding={4}>
321
- {importErrors.map((err, index) => (
322
- <Box key={index} paddingBottom={2}>
323
- <Typography textColor="danger600">
324
- {index + 1}. {err}
325
- </Typography>
326
- </Box>
327
- ))}
328
- </Box>
329
- </Modal.Body>
330
-
331
- <Modal.Footer
332
- endActions={
333
- <Button onClick={() => setShowErrorModal(false)}>
334
- Close
335
- </Button>
336
- }
337
- />
338
- </Modal.Content>
339
- </Modal.Root>
340
- )}
341
- </>
342
319
  );
343
320
  };
344
321
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nkhang1902/strapi-plugin-export-import-clsx",
3
- "version": "1.1.153",
3
+ "version": "1.2.0",
4
4
  "description": "A powerful Strapi plugin for exporting and importing data with Excel support and advanced filtering",
5
5
  "main": "./strapi-server.js",
6
6
  "scripts": {
@@ -14,7 +14,10 @@ module.exports = ({ strapi }) => ({
14
14
  .plugin("export-import-clsx")
15
15
  .service("import-service");
16
16
 
17
- const { eventId } = ctx.request.query;
17
+ let { eventId } = ctx.request.query;
18
+ if (eventId == "") {
19
+ eventId = null;
20
+ }
18
21
 
19
22
  const result = await importService.importData(file, targetContentType, eventId);
20
23
 
@@ -298,7 +298,7 @@ module.exports = ({ strapi }) => ({
298
298
 
299
299
  if (componentFields.includes(key)) {
300
300
  for (const subKey in value) {
301
- if (["id", "createdAt", "updatedAt", "lastUpdate", "passcode"]) continue;
301
+ if (["id", "createdAt", "updatedAt", "lastUpdate", "passcode"].includes(subKey)) continue;
302
302
  result[`${key}_${subKey}`] = value[subKey];
303
303
  }
304
304
  continue;
@@ -147,7 +147,7 @@ module.exports = ({ strapi }) => ({
147
147
  .map(([fieldName, attr]) => toCamel(fieldName));
148
148
  },
149
149
 
150
- async handleRelations(entry, contentType, eventId) {
150
+ async handleRelations(entry, contentType) {
151
151
  const resolveRelationValue = async (field, value, target) => {
152
152
  const targetAttr = strapi.contentTypes[target].attributes;
153
153
  for (const field of SHORTCUT_FIELDS) {
@@ -156,7 +156,7 @@ module.exports = ({ strapi }) => ({
156
156
  filters: { [field]: { $eq: value } },
157
157
  });
158
158
  if (existing) return { id: existing.id };
159
- throw new Error(`Data with ${field} ${value} not found. Cannot map to ${contentType}`);
159
+ throw new Error(`Not found.`);
160
160
  }
161
161
  return null;
162
162
  };
@@ -181,11 +181,7 @@ module.exports = ({ strapi }) => ({
181
181
  if (relation === "manyToMany" || relation === "oneToMany") {
182
182
  updatedEntry[field] = [];
183
183
  } else {
184
- if (field === 'event') {
185
- updatedEntry[field] = { documentId: eventId };
186
- } else {
187
- updatedEntry[field] = null;
188
- }
184
+ updatedEntry[field] = null;
189
185
  }
190
186
  continue;
191
187
  }
@@ -215,7 +211,7 @@ module.exports = ({ strapi }) => ({
215
211
  updatedEntry[field] = Array.isArray(value) ? processed : processed[0];
216
212
  } catch (err) {
217
213
  throw new Error(
218
- `Failed processing field ${field} with value ${JSON.stringify(value)}: ${err.message}`
214
+ `Error resolving field "${field}" with value "${value}": ${err.message}`
219
215
  );
220
216
  }
221
217
  }
@@ -394,76 +390,74 @@ module.exports = ({ strapi }) => ({
394
390
 
395
391
  async importEntries(entries, contentType, eventId) {
396
392
  const results = { created: 0, updated: 0, errors: [] };
397
-
398
- await strapi.db.transaction(async ({ trx, rollback, onRollback }) => {
399
- onRollback(() => {
400
- strapi.log.error("Transaction rolled back due to an error!");
401
- strapi.log.error(results.errors);
402
- });
403
-
393
+
394
+ await strapi.db.transaction(async ({ trx }) => {
395
+ let event = null;
396
+
397
+ if (eventId) {
398
+ event = await strapi.documents("api::event.event").findFirst(
399
+ { filters: { documentId: eventId } },
400
+ { transaction: trx }
401
+ );
402
+ }
403
+
404
+ let shouldRollback = false;
405
+
404
406
  for (let i = 0; i < entries.length; i++) {
405
407
  const entry = entries[i];
406
408
  let existing = null;
407
-
409
+
408
410
  try {
409
411
  let { id, ...data } = entry;
410
-
411
- // Check if document exists
412
- if (id && id !== "null" && id !== "undefined") {
412
+
413
+ if (id) {
413
414
  existing = await strapi.documents(contentType).findFirst(
414
- {
415
- filters: { id },
416
- populate: "*",
417
- },
415
+ { filters: { id }, populate: "*" },
418
416
  { transaction: trx }
419
417
  );
420
- if (!existing) {
421
- throw new Error(`Document with id ${id} not found`);
422
- }
418
+ if (!existing) throw new Error(`Document with id ${id} not found`);
423
419
  }
424
420
 
425
- // Handle relations & components
426
- data = await this.handleRelations(data, contentType, eventId);
421
+ if (event && !data.event) data.event = event.name;
422
+
423
+ data = await this.handleRelations(data, contentType);
427
424
  data = await this.handleComponents(data, existing, contentType);
425
+
428
426
  const sanitizeErrors = [];
429
427
  data = this.sanitizeEntryBeforeWrite(data, contentType, '', sanitizeErrors);
430
-
431
428
  if (sanitizeErrors.length) {
432
- throw new Error(`Data validation failed:\n${sanitizeErrors.join('\n')}`);
429
+ throw new Error(sanitizeErrors.join('\n'));
433
430
  }
434
431
 
435
- // Update
436
432
  if (existing) {
437
433
  await strapi.documents(contentType).update(
438
- {
439
- documentId: existing.documentId,
440
- data,
441
- },
434
+ { documentId: existing.documentId, data },
442
435
  { transaction: trx }
443
436
  );
444
437
  results.updated++;
445
438
  } else {
446
- // Create
447
- await strapi
448
- .documents(contentType)
449
- .create({ data }, { transaction: trx });
439
+ await strapi.documents(contentType).create(
440
+ { data },
441
+ { transaction: trx }
442
+ );
450
443
  results.created++;
451
444
  }
452
445
  } catch (err) {
453
- results.errors.push(
454
- `Failed ${existing ? "updating" : "creating"} on row ${
455
- i + 2
456
- }: ${err.message}`
457
- );
458
- results.created = 0;
459
- results.updated = 0;
460
-
461
- // IMPORTANT: force rollback
462
- throw err;
446
+ shouldRollback = true;
447
+ results.errors.push(`Row ${i + 2}: ${err.message}`);
463
448
  }
464
449
  }
450
+
451
+ if (shouldRollback) {
452
+ await trx.rollback();
453
+ }
465
454
  });
466
455
 
456
+ if (results.errors.length) {
457
+ results.created = 0;
458
+ results.updated = 0;
459
+ }
460
+
467
461
  return results;
468
462
  },
469
463
  });