@nkhang1902/strapi-plugin-export-import-clsx 1.0.4 → 1.1.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/admin/src/components/ExportImportButtons/index.jsx +281 -0
- package/package.json +3 -3
- package/server/controllers/export-controller.js +56 -43
- package/server/controllers/import-controller.js +14 -12
- package/server/services/export-service.js +160 -116
- package/server/services/import-service.js +344 -309
- package/strapi-admin.js +10 -69
- package/admin/src/components/BulkActions/index.js +0 -70
- package/admin/src/components/ExportButton/index.js +0 -48
- package/admin/src/components/ExportImportButtons/index.js +0 -279
- package/admin/src/components/ImportButton/index.js +0 -54
- package/admin/src/translations/en.json +0 -14
package/strapi-admin.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import ExportImportButtons from './admin/src/components/ExportImportButtons';
|
|
1
|
+
import pluginPkg from "./package.json";
|
|
2
|
+
import pluginId from "./admin/src/pluginId";
|
|
3
|
+
import Initializer from "./admin/src/components/Initializer";
|
|
4
|
+
import ExportImportButtons from "./admin/src/components/ExportImportButtons";
|
|
6
5
|
|
|
7
6
|
const name = pluginPkg.strapi.name;
|
|
8
7
|
|
|
@@ -19,70 +18,12 @@ export default {
|
|
|
19
18
|
},
|
|
20
19
|
|
|
21
20
|
bootstrap(app) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
name: 'export-import-buttons',
|
|
28
|
-
Component: ExportImportButtons,
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
// Method 2: Plugin-based injection
|
|
32
|
-
else if (app.getPlugin) {
|
|
33
|
-
const contentManager = app.getPlugin('content-manager');
|
|
34
|
-
if (contentManager && contentManager.injectComponent) {
|
|
35
|
-
contentManager.injectComponent('listView', 'actions', {
|
|
36
|
-
name: 'export-import-buttons',
|
|
37
|
-
Component: ExportImportButtons,
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
// Method 3: Global injection
|
|
42
|
-
else if (app.addComponent) {
|
|
43
|
-
app.addComponent('content-manager.listView.actions', ExportImportButtons);
|
|
44
|
-
}
|
|
45
|
-
} catch (error) {
|
|
46
|
-
console.warn('Failed to inject export-import buttons:', error);
|
|
47
|
-
|
|
48
|
-
// Fallback: Add as menu item if injection fails
|
|
49
|
-
app.addMenuLink({
|
|
50
|
-
to: `/plugins/${pluginId}`,
|
|
51
|
-
icon: () => React.createElement('span', null, '📊'),
|
|
52
|
-
intlLabel: {
|
|
53
|
-
id: `${pluginId}.plugin.name`,
|
|
54
|
-
defaultMessage: 'Export Import',
|
|
55
|
-
},
|
|
56
|
-
Component: async () => {
|
|
57
|
-
const component = await import('./admin/src/pages/App');
|
|
58
|
-
return component;
|
|
59
|
-
},
|
|
60
|
-
permissions: [],
|
|
21
|
+
const contentManager = app.getPlugin("content-manager");
|
|
22
|
+
if (contentManager && contentManager.injectComponent) {
|
|
23
|
+
contentManager.injectComponent("listView", "actions", {
|
|
24
|
+
name: "export-import-buttons",
|
|
25
|
+
Component: ExportImportButtons,
|
|
61
26
|
});
|
|
62
27
|
}
|
|
63
28
|
},
|
|
64
|
-
|
|
65
|
-
async registerTrads(app) {
|
|
66
|
-
const { locales } = app;
|
|
67
|
-
|
|
68
|
-
const importedTrads = await Promise.all(
|
|
69
|
-
locales.map((locale) => {
|
|
70
|
-
return import(`./admin/src/translations/${locale}.json`)
|
|
71
|
-
.then(({ default: data }) => {
|
|
72
|
-
return {
|
|
73
|
-
data: data,
|
|
74
|
-
locale,
|
|
75
|
-
};
|
|
76
|
-
})
|
|
77
|
-
.catch(() => {
|
|
78
|
-
return {
|
|
79
|
-
data: {},
|
|
80
|
-
locale,
|
|
81
|
-
};
|
|
82
|
-
});
|
|
83
|
-
})
|
|
84
|
-
);
|
|
85
|
-
|
|
86
|
-
return Promise.resolve(importedTrads);
|
|
87
|
-
},
|
|
88
|
-
};
|
|
29
|
+
};
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import ExportButton from '../ExportButton';
|
|
3
|
-
import ImportButton from '../ImportButton';
|
|
4
|
-
|
|
5
|
-
const BulkActions = ({ layout }) => {
|
|
6
|
-
const handleExportAll = async () => {
|
|
7
|
-
try {
|
|
8
|
-
const contentType = layout.uid;
|
|
9
|
-
|
|
10
|
-
// Get current filters from URL if any
|
|
11
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
12
|
-
const filters = {};
|
|
13
|
-
|
|
14
|
-
// Build filters from URL params
|
|
15
|
-
for (const [key, value] of urlParams.entries()) {
|
|
16
|
-
if (key.startsWith('filters[')) {
|
|
17
|
-
filters[key] = value;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const queryString = new URLSearchParams({
|
|
22
|
-
format: 'excel',
|
|
23
|
-
contentType: contentType,
|
|
24
|
-
...filters
|
|
25
|
-
}).toString();
|
|
26
|
-
|
|
27
|
-
const response = await fetch(`/export-import-clsx/export?${queryString}`);
|
|
28
|
-
|
|
29
|
-
if (response.ok) {
|
|
30
|
-
const blob = await response.blob();
|
|
31
|
-
const url = window.URL.createObjectURL(blob);
|
|
32
|
-
const a = document.createElement('a');
|
|
33
|
-
a.href = url;
|
|
34
|
-
a.download = `${contentType.replace('api::', '')}-export-${new Date().toISOString().split('T')[0]}.xlsx`;
|
|
35
|
-
document.body.appendChild(a);
|
|
36
|
-
a.click();
|
|
37
|
-
window.URL.revokeObjectURL(url);
|
|
38
|
-
document.body.removeChild(a);
|
|
39
|
-
} else {
|
|
40
|
-
throw new Error('Export failed');
|
|
41
|
-
}
|
|
42
|
-
} catch (error) {
|
|
43
|
-
alert('Export failed: ' + error.message);
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
return React.createElement('div', {
|
|
48
|
-
style: {
|
|
49
|
-
display: 'flex',
|
|
50
|
-
gap: '8px',
|
|
51
|
-
alignItems: 'center',
|
|
52
|
-
marginLeft: '16px'
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
React.createElement('button', {
|
|
56
|
-
onClick: handleExportAll,
|
|
57
|
-
style: {
|
|
58
|
-
padding: '8px 16px',
|
|
59
|
-
backgroundColor: '#4945ff',
|
|
60
|
-
color: 'white',
|
|
61
|
-
border: 'none',
|
|
62
|
-
borderRadius: '4px',
|
|
63
|
-
cursor: 'pointer'
|
|
64
|
-
}
|
|
65
|
-
}, 'Export All'),
|
|
66
|
-
React.createElement(ImportButton)
|
|
67
|
-
);
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
export default BulkActions;
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
|
|
3
|
-
const ExportButton = ({ layout, modifiedData }) => {
|
|
4
|
-
const handleExport = async () => {
|
|
5
|
-
try {
|
|
6
|
-
const contentType = layout.uid;
|
|
7
|
-
const entryId = modifiedData.id;
|
|
8
|
-
|
|
9
|
-
if (!entryId) {
|
|
10
|
-
alert('Please save the entry first');
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const response = await fetch(`/export-import-clsx/export/${contentType}/${entryId}`);
|
|
15
|
-
|
|
16
|
-
if (response.ok) {
|
|
17
|
-
const blob = await response.blob();
|
|
18
|
-
const url = window.URL.createObjectURL(blob);
|
|
19
|
-
const a = document.createElement('a');
|
|
20
|
-
a.href = url;
|
|
21
|
-
a.download = `entry-${entryId}-${new Date().toISOString().split('T')[0]}.xlsx`;
|
|
22
|
-
document.body.appendChild(a);
|
|
23
|
-
a.click();
|
|
24
|
-
window.URL.revokeObjectURL(url);
|
|
25
|
-
document.body.removeChild(a);
|
|
26
|
-
} else {
|
|
27
|
-
throw new Error('Export failed');
|
|
28
|
-
}
|
|
29
|
-
} catch (error) {
|
|
30
|
-
alert('Export failed: ' + error.message);
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
return React.createElement('button', {
|
|
35
|
-
onClick: handleExport,
|
|
36
|
-
style: {
|
|
37
|
-
padding: '8px 16px',
|
|
38
|
-
backgroundColor: '#4945ff',
|
|
39
|
-
color: 'white',
|
|
40
|
-
border: 'none',
|
|
41
|
-
borderRadius: '4px',
|
|
42
|
-
cursor: 'pointer',
|
|
43
|
-
marginLeft: '8px'
|
|
44
|
-
}
|
|
45
|
-
}, 'Export Entry');
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
export default ExportButton;
|
|
@@ -1,279 +0,0 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
2
|
-
|
|
3
|
-
const ExportImportButtons = (props) => {
|
|
4
|
-
const [isExporting, setIsExporting] = useState(false);
|
|
5
|
-
const [isImporting, setIsImporting] = useState(false);
|
|
6
|
-
|
|
7
|
-
// Get current content type from props or URL
|
|
8
|
-
const getContentType = () => {
|
|
9
|
-
if (props.layout?.uid) {
|
|
10
|
-
return props.layout.uid;
|
|
11
|
-
}
|
|
12
|
-
// Fallback: extract from URL
|
|
13
|
-
const path = window.location.pathname;
|
|
14
|
-
const match = path.match(/\/admin\/content-manager\/collection-types\/([^\/]+)/);
|
|
15
|
-
return match ? match[1] : null;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
// Get current filters from URL
|
|
19
|
-
const getCurrentFilters = () => {
|
|
20
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
21
|
-
const filters = {};
|
|
22
|
-
|
|
23
|
-
for (const [key, value] of urlParams.entries()) {
|
|
24
|
-
if (key.startsWith('filters[') || key === 'sort' || key === 'page' || key === 'pageSize' || key === 'locale' || key === '_q') {
|
|
25
|
-
filters[key] = value;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return filters;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
// Get selected entries from props
|
|
33
|
-
const getSelectedEntries = () => {
|
|
34
|
-
// Try to get selected entries from various possible props
|
|
35
|
-
if (props.selectedEntries && props.selectedEntries.length > 0) {
|
|
36
|
-
return props.selectedEntries;
|
|
37
|
-
}
|
|
38
|
-
if (props.selected && props.selected.length > 0) {
|
|
39
|
-
return props.selected;
|
|
40
|
-
}
|
|
41
|
-
if (props.selection && props.selection.length > 0) {
|
|
42
|
-
return props.selection;
|
|
43
|
-
}
|
|
44
|
-
const selectedIds = [];
|
|
45
|
-
let field = '';
|
|
46
|
-
const getHeaderKey = i => {
|
|
47
|
-
const el = document.querySelector(`thead th:nth-child(${i}) button, thead th:nth-child(${i}) span`);
|
|
48
|
-
if (!el) return '';
|
|
49
|
-
const parts = el.textContent.trim().split(/\s+/);
|
|
50
|
-
return parts.pop(); // last word
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
const rows = document.querySelectorAll('tbody tr');
|
|
55
|
-
const allowedFields = [
|
|
56
|
-
'id', 'name', 'title', 'tickerCode',
|
|
57
|
-
'fullName', 'email', 'businessEmail',
|
|
58
|
-
'telephone', 'mobile'
|
|
59
|
-
];
|
|
60
|
-
|
|
61
|
-
let foundIndex = null;
|
|
62
|
-
|
|
63
|
-
for (let i = 1; i <= 10; i++) {
|
|
64
|
-
const headerBtn = getHeaderKey(i);
|
|
65
|
-
if (headerBtn !== '' && allowedFields.includes(headerBtn)) {
|
|
66
|
-
field = headerBtn;
|
|
67
|
-
foundIndex = i;
|
|
68
|
-
break;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (!foundIndex) {
|
|
73
|
-
console.warn('No valid header column found');
|
|
74
|
-
return [[], ''];
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// gather values for selected rows
|
|
78
|
-
rows.forEach(row => {
|
|
79
|
-
const checkbox = row.querySelector('td:nth-child(1) button[role="checkbox"]');
|
|
80
|
-
if (checkbox?.getAttribute('aria-checked') === 'true') {
|
|
81
|
-
const cellSpan = row.querySelector(`td:nth-child(${foundIndex}) span`);
|
|
82
|
-
const text = cellSpan?.textContent.trim();
|
|
83
|
-
if (text) selectedIds.push(text);
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
return [selectedIds, field];
|
|
88
|
-
|
|
89
|
-
} catch (e) {
|
|
90
|
-
console.error(e);
|
|
91
|
-
return [[], ''];
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
const handleExport = async () => {
|
|
96
|
-
const contentType = getContentType();
|
|
97
|
-
if (!contentType) {
|
|
98
|
-
alert('Could not determine content type');
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
setIsExporting(true);
|
|
103
|
-
try {
|
|
104
|
-
const filters = getCurrentFilters();
|
|
105
|
-
const [selectedEntries, selectedField] = getSelectedEntries();
|
|
106
|
-
|
|
107
|
-
const queryParams = new URLSearchParams({
|
|
108
|
-
format: 'excel',
|
|
109
|
-
contentType: contentType,
|
|
110
|
-
...filters
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
// Add selected IDs if any
|
|
114
|
-
if (selectedEntries.length > 0) {
|
|
115
|
-
queryParams.set('selectedIds', JSON.stringify(selectedEntries));
|
|
116
|
-
queryParams.set('selectedField', selectedField);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const response = await fetch(`/export-import-clsx/export?${queryParams}`);
|
|
120
|
-
|
|
121
|
-
if (response.ok) {
|
|
122
|
-
const blob = await response.blob();
|
|
123
|
-
const url = window.URL.createObjectURL(blob);
|
|
124
|
-
const a = document.createElement('a');
|
|
125
|
-
a.href = url;
|
|
126
|
-
|
|
127
|
-
// Set filename based on selection
|
|
128
|
-
const filename = selectedEntries.length > 0
|
|
129
|
-
? `${contentType.replace('api::', '')}-selected-${selectedEntries.length}-${new Date().toISOString().split('T')[0]}.xlsx`
|
|
130
|
-
: `${contentType.replace('api::', '')}-export-${new Date().toISOString().split('T')[0]}.xlsx`;
|
|
131
|
-
|
|
132
|
-
a.download = filename;
|
|
133
|
-
document.body.appendChild(a);
|
|
134
|
-
a.click();
|
|
135
|
-
window.URL.revokeObjectURL(url);
|
|
136
|
-
document.body.removeChild(a);
|
|
137
|
-
} else {
|
|
138
|
-
throw new Error('Export failed');
|
|
139
|
-
}
|
|
140
|
-
} catch (error) {
|
|
141
|
-
alert('Export failed: ' + error.message);
|
|
142
|
-
} finally {
|
|
143
|
-
setIsExporting(false);
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
const handleImport = async (event) => {
|
|
148
|
-
const file = event.target.files[0];
|
|
149
|
-
if (!file) return;
|
|
150
|
-
|
|
151
|
-
const contentType = getContentType();
|
|
152
|
-
if (!contentType) {
|
|
153
|
-
alert('Could not determine content type');
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
setIsImporting(true);
|
|
158
|
-
const formData = new FormData();
|
|
159
|
-
formData.append('file', file);
|
|
160
|
-
formData.append('contentType', contentType);
|
|
161
|
-
|
|
162
|
-
try {
|
|
163
|
-
const response = await fetch('/export-import-clsx/import', {
|
|
164
|
-
method: 'POST',
|
|
165
|
-
body: formData,
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
if (response.ok) {
|
|
169
|
-
const result = await response.json();
|
|
170
|
-
|
|
171
|
-
// Create simple, human message
|
|
172
|
-
const created = result.summary?.created || result.result.created;
|
|
173
|
-
const updated = result.summary?.updated || result.result.updated;
|
|
174
|
-
const errors = result.result.errors?.length || 0;
|
|
175
|
-
|
|
176
|
-
const total = created + updated;
|
|
177
|
-
let message = 'Import completed!\n\n';
|
|
178
|
-
|
|
179
|
-
if (total > 0) {
|
|
180
|
-
message += `Processed ${total} ${total === 1 ? 'entry' : 'entries'}\n`;
|
|
181
|
-
if (created > 0) {
|
|
182
|
-
message += `• Created: ${created}\n`;
|
|
183
|
-
}
|
|
184
|
-
if (updated > 0) {
|
|
185
|
-
message += `• Updated: ${updated}\n`;
|
|
186
|
-
}
|
|
187
|
-
} else if (errors === 0) {
|
|
188
|
-
message += 'No changes were made\n';
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (errors > 0) {
|
|
192
|
-
message += `\nFound ${errors} ${errors === 1 ? 'error' : 'errors'}:\n`;
|
|
193
|
-
result.result.errors.slice(0, 2).forEach((error, index) => {
|
|
194
|
-
message += `• ${error}\n`;
|
|
195
|
-
});
|
|
196
|
-
if (errors > 2) {
|
|
197
|
-
message += `• ... and ${errors - 2} more\n`;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
alert(message);
|
|
202
|
-
|
|
203
|
-
// Reload the page to show new data
|
|
204
|
-
window.location.reload();
|
|
205
|
-
} else {
|
|
206
|
-
const error = await response.json();
|
|
207
|
-
throw new Error(error.error || 'Import failed');
|
|
208
|
-
}
|
|
209
|
-
} catch (error) {
|
|
210
|
-
alert('Import failed: ' + error.message);
|
|
211
|
-
} finally {
|
|
212
|
-
setIsImporting(false);
|
|
213
|
-
event.target.value = '';
|
|
214
|
-
}
|
|
215
|
-
};
|
|
216
|
-
|
|
217
|
-
const [selectedEntries, selectedField] = getSelectedEntries();
|
|
218
|
-
const exportButtonText = isExporting
|
|
219
|
-
? 'Exporting...'
|
|
220
|
-
: selectedEntries.length > 0
|
|
221
|
-
? `Export (${selectedEntries.length})`
|
|
222
|
-
: 'Export';
|
|
223
|
-
|
|
224
|
-
return React.createElement('div', {
|
|
225
|
-
style: {
|
|
226
|
-
display: 'flex',
|
|
227
|
-
gap: '8px',
|
|
228
|
-
alignItems: 'center',
|
|
229
|
-
marginRight: '16px',
|
|
230
|
-
order: -1 // This will place it before other elements
|
|
231
|
-
}
|
|
232
|
-
},
|
|
233
|
-
// Export Button
|
|
234
|
-
React.createElement('button', {
|
|
235
|
-
onClick: handleExport,
|
|
236
|
-
disabled: isExporting,
|
|
237
|
-
style: {
|
|
238
|
-
padding: '8px 16px',
|
|
239
|
-
backgroundColor: isExporting ? '#dcdce4' : '#4945ff',
|
|
240
|
-
color: 'white',
|
|
241
|
-
border: 'none',
|
|
242
|
-
borderRadius: '4px',
|
|
243
|
-
fontSize: '14px',
|
|
244
|
-
fontWeight: '500',
|
|
245
|
-
cursor: isExporting ? 'not-allowed' : 'pointer',
|
|
246
|
-
transition: 'background-color 0.2s'
|
|
247
|
-
}
|
|
248
|
-
}, exportButtonText),
|
|
249
|
-
|
|
250
|
-
// Import Button - same color as Export
|
|
251
|
-
React.createElement('div', { style: { position: 'relative' } },
|
|
252
|
-
React.createElement('input', {
|
|
253
|
-
type: 'file',
|
|
254
|
-
accept: '.xlsx,.xls,.json',
|
|
255
|
-
onChange: handleImport,
|
|
256
|
-
disabled: isImporting,
|
|
257
|
-
style: { display: 'none' },
|
|
258
|
-
id: 'import-file-input'
|
|
259
|
-
}),
|
|
260
|
-
React.createElement('label', {
|
|
261
|
-
htmlFor: 'import-file-input',
|
|
262
|
-
style: {
|
|
263
|
-
display: 'inline-block',
|
|
264
|
-
padding: '8px 16px',
|
|
265
|
-
backgroundColor: isImporting ? '#dcdce4' : '#4945ff', // Same color as Export
|
|
266
|
-
color: 'white',
|
|
267
|
-
border: 'none',
|
|
268
|
-
borderRadius: '4px',
|
|
269
|
-
fontSize: '14px',
|
|
270
|
-
fontWeight: '500',
|
|
271
|
-
cursor: isImporting ? 'not-allowed' : 'pointer',
|
|
272
|
-
transition: 'background-color 0.2s'
|
|
273
|
-
}
|
|
274
|
-
}, isImporting ? 'Importing...' : 'Import')
|
|
275
|
-
)
|
|
276
|
-
);
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
export default ExportImportButtons;
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
|
|
3
|
-
const ImportButton = () => {
|
|
4
|
-
const handleImport = async (event) => {
|
|
5
|
-
const file = event.target.files[0];
|
|
6
|
-
if (!file) return;
|
|
7
|
-
|
|
8
|
-
const formData = new FormData();
|
|
9
|
-
formData.append('file', file);
|
|
10
|
-
|
|
11
|
-
try {
|
|
12
|
-
const response = await fetch('/export-import-clsx/import', {
|
|
13
|
-
method: 'POST',
|
|
14
|
-
body: formData,
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
if (response.ok) {
|
|
18
|
-
const result = await response.json();
|
|
19
|
-
alert(`Import completed! Imported: ${result.result.imported}, Errors: ${result.result.errors.length}`);
|
|
20
|
-
window.location.reload();
|
|
21
|
-
} else {
|
|
22
|
-
throw new Error('Import failed');
|
|
23
|
-
}
|
|
24
|
-
} catch (error) {
|
|
25
|
-
alert('Import failed: ' + error.message);
|
|
26
|
-
} finally {
|
|
27
|
-
event.target.value = '';
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
return React.createElement('div', { style: { display: 'inline-block', marginLeft: '8px' } },
|
|
32
|
-
React.createElement('input', {
|
|
33
|
-
type: 'file',
|
|
34
|
-
accept: '.xlsx,.xls,.json',
|
|
35
|
-
onChange: handleImport,
|
|
36
|
-
style: { display: 'none' },
|
|
37
|
-
id: 'import-file-input'
|
|
38
|
-
}),
|
|
39
|
-
React.createElement('label', {
|
|
40
|
-
htmlFor: 'import-file-input',
|
|
41
|
-
style: {
|
|
42
|
-
display: 'inline-block',
|
|
43
|
-
padding: '8px 16px',
|
|
44
|
-
backgroundColor: '#328048',
|
|
45
|
-
color: 'white',
|
|
46
|
-
border: 'none',
|
|
47
|
-
borderRadius: '4px',
|
|
48
|
-
cursor: 'pointer'
|
|
49
|
-
}
|
|
50
|
-
}, 'Import Data')
|
|
51
|
-
);
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
export default ImportButton;
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"plugin.name": "Export Import CLSX",
|
|
3
|
-
"plugin.description": "Export and import data with enhanced functionality",
|
|
4
|
-
"export.title": "Export Data",
|
|
5
|
-
"export.description": "Export all your content types data to a JSON file",
|
|
6
|
-
"export.button": "Export Data",
|
|
7
|
-
"export.success": "Export completed successfully",
|
|
8
|
-
"export.error": "Export failed",
|
|
9
|
-
"import.title": "Import Data",
|
|
10
|
-
"import.description": "Import data from a JSON file to your Strapi instance",
|
|
11
|
-
"import.button": "Import Data",
|
|
12
|
-
"import.success": "Import completed successfully",
|
|
13
|
-
"import.error": "Import failed"
|
|
14
|
-
}
|