@tunghtml/strapi-plugin-export-import-clsx 1.0.1 → 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/strapi-admin.js CHANGED
@@ -1,8 +1,8 @@
1
- import React from 'react';
2
- import pluginPkg from './package.json';
3
- import pluginId from './admin/src/pluginId';
4
- import Initializer from './admin/src/components/Initializer';
5
- import ExportImportButtons from './admin/src/components/ExportImportButtons';
1
+ import React from "react";
2
+ import pluginPkg from "./package.json";
3
+ import pluginId from "./admin/src/pluginId";
4
+ import Initializer from "./admin/src/components/Initializer";
5
+ import ExportImportButtons from "./admin/src/components/ExportImportButtons";
6
6
 
7
7
  const name = pluginPkg.strapi.name;
8
8
 
@@ -23,38 +23,41 @@ export default {
23
23
  try {
24
24
  // Method 1: Direct injection
25
25
  if (app.injectContentManagerComponent) {
26
- app.injectContentManagerComponent('listView', 'actions', {
27
- name: 'export-import-buttons',
26
+ app.injectContentManagerComponent("listView", "actions", {
27
+ name: "export-import-buttons",
28
28
  Component: ExportImportButtons,
29
29
  });
30
30
  }
31
31
  // Method 2: Plugin-based injection
32
32
  else if (app.getPlugin) {
33
- const contentManager = app.getPlugin('content-manager');
33
+ const contentManager = app.getPlugin("content-manager");
34
34
  if (contentManager && contentManager.injectComponent) {
35
- contentManager.injectComponent('listView', 'actions', {
36
- name: 'export-import-buttons',
35
+ contentManager.injectComponent("listView", "actions", {
36
+ name: "export-import-buttons",
37
37
  Component: ExportImportButtons,
38
38
  });
39
39
  }
40
40
  }
41
41
  // Method 3: Global injection
42
42
  else if (app.addComponent) {
43
- app.addComponent('content-manager.listView.actions', ExportImportButtons);
43
+ app.addComponent(
44
+ "content-manager.listView.actions",
45
+ ExportImportButtons
46
+ );
44
47
  }
45
48
  } catch (error) {
46
- console.warn('Failed to inject export-import buttons:', error);
47
-
49
+ console.warn("Failed to inject export-import buttons:", error);
50
+
48
51
  // Fallback: Add as menu item if injection fails
49
52
  app.addMenuLink({
50
53
  to: `/plugins/${pluginId}`,
51
- icon: () => React.createElement('span', null, '📊'),
54
+ icon: () => React.createElement("span", null, "📊"),
52
55
  intlLabel: {
53
56
  id: `${pluginId}.plugin.name`,
54
- defaultMessage: 'Export Import',
57
+ defaultMessage: "Export Import",
55
58
  },
56
59
  Component: async () => {
57
- const component = await import('./admin/src/pages/App');
60
+ const component = await import("./admin/src/pages/App");
58
61
  return component;
59
62
  },
60
63
  permissions: [],
@@ -85,4 +88,4 @@ export default {
85
88
 
86
89
  return Promise.resolve(importedTrads);
87
90
  },
88
- };
91
+ };
@@ -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,245 +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') {
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
-
45
- // Try to get from global state or context
46
- try {
47
- // Check if there's a selection in the page context
48
- const checkboxes = document.querySelectorAll('input[type="checkbox"]:checked');
49
- const selectedIds = [];
50
- checkboxes.forEach(checkbox => {
51
- const value = checkbox.value;
52
- if (value && value !== 'on' && value !== 'all') {
53
- selectedIds.push(value);
54
- }
55
- });
56
- return selectedIds;
57
- } catch (error) {
58
- return [];
59
- }
60
- };
61
-
62
- const handleExport = async () => {
63
- const contentType = getContentType();
64
- if (!contentType) {
65
- alert('Could not determine content type');
66
- return;
67
- }
68
-
69
- setIsExporting(true);
70
- try {
71
- const filters = getCurrentFilters();
72
- const selectedEntries = getSelectedEntries();
73
-
74
- const queryParams = new URLSearchParams({
75
- format: 'excel',
76
- contentType: contentType,
77
- ...filters
78
- });
79
-
80
- // Add selected IDs if any
81
- if (selectedEntries.length > 0) {
82
- queryParams.set('selectedIds', JSON.stringify(selectedEntries));
83
- }
84
-
85
- const response = await fetch(`/export-import-clsx/export?${queryParams}`);
86
-
87
- if (response.ok) {
88
- const blob = await response.blob();
89
- const url = window.URL.createObjectURL(blob);
90
- const a = document.createElement('a');
91
- a.href = url;
92
-
93
- // Set filename based on selection
94
- const filename = selectedEntries.length > 0
95
- ? `${contentType.replace('api::', '')}-selected-${selectedEntries.length}-${new Date().toISOString().split('T')[0]}.xlsx`
96
- : `${contentType.replace('api::', '')}-export-${new Date().toISOString().split('T')[0]}.xlsx`;
97
-
98
- a.download = filename;
99
- document.body.appendChild(a);
100
- a.click();
101
- window.URL.revokeObjectURL(url);
102
- document.body.removeChild(a);
103
- } else {
104
- throw new Error('Export failed');
105
- }
106
- } catch (error) {
107
- alert('Export failed: ' + error.message);
108
- } finally {
109
- setIsExporting(false);
110
- }
111
- };
112
-
113
- const handleImport = async (event) => {
114
- const file = event.target.files[0];
115
- if (!file) return;
116
-
117
- const contentType = getContentType();
118
- if (!contentType) {
119
- alert('Could not determine content type');
120
- return;
121
- }
122
-
123
- setIsImporting(true);
124
- const formData = new FormData();
125
- formData.append('file', file);
126
- formData.append('contentType', contentType);
127
-
128
- try {
129
- const response = await fetch('/export-import-clsx/import', {
130
- method: 'POST',
131
- body: formData,
132
- });
133
-
134
- if (response.ok) {
135
- const result = await response.json();
136
-
137
- // Create simple, human message
138
- const created = result.summary?.created || result.result.created;
139
- const updated = result.summary?.updated || result.result.updated;
140
- const errors = result.result.errors?.length || 0;
141
-
142
- const total = created + updated;
143
- let message = 'Import completed!\n\n';
144
-
145
- if (total > 0) {
146
- message += `Processed ${total} ${total === 1 ? 'entry' : 'entries'}\n`;
147
- if (created > 0) {
148
- message += `• Created: ${created}\n`;
149
- }
150
- if (updated > 0) {
151
- message += `• Updated: ${updated}\n`;
152
- }
153
- } else if (errors === 0) {
154
- message += 'No changes were made\n';
155
- }
156
-
157
- if (errors > 0) {
158
- message += `\nFound ${errors} ${errors === 1 ? 'error' : 'errors'}:\n`;
159
- result.result.errors.slice(0, 2).forEach((error, index) => {
160
- message += `• ${error}\n`;
161
- });
162
- if (errors > 2) {
163
- message += `• ... and ${errors - 2} more\n`;
164
- }
165
- }
166
-
167
- alert(message);
168
-
169
- // Reload the page to show new data
170
- window.location.reload();
171
- } else {
172
- const error = await response.json();
173
- throw new Error(error.error || 'Import failed');
174
- }
175
- } catch (error) {
176
- alert('Import failed: ' + error.message);
177
- } finally {
178
- setIsImporting(false);
179
- event.target.value = '';
180
- }
181
- };
182
-
183
- const selectedEntries = getSelectedEntries();
184
- const exportButtonText = isExporting
185
- ? 'Exporting...'
186
- : selectedEntries.length > 0
187
- ? `Export (${selectedEntries.length})`
188
- : 'Export';
189
-
190
- return React.createElement('div', {
191
- style: {
192
- display: 'flex',
193
- gap: '8px',
194
- alignItems: 'center',
195
- marginRight: '16px',
196
- order: -1 // This will place it before other elements
197
- }
198
- },
199
- // Export Button
200
- React.createElement('button', {
201
- onClick: handleExport,
202
- disabled: isExporting,
203
- style: {
204
- padding: '8px 16px',
205
- backgroundColor: isExporting ? '#dcdce4' : '#4945ff',
206
- color: 'white',
207
- border: 'none',
208
- borderRadius: '4px',
209
- fontSize: '14px',
210
- fontWeight: '500',
211
- cursor: isExporting ? 'not-allowed' : 'pointer',
212
- transition: 'background-color 0.2s'
213
- }
214
- }, exportButtonText),
215
-
216
- // Import Button - same color as Export
217
- React.createElement('div', { style: { position: 'relative' } },
218
- React.createElement('input', {
219
- type: 'file',
220
- accept: '.xlsx,.xls,.json',
221
- onChange: handleImport,
222
- disabled: isImporting,
223
- style: { display: 'none' },
224
- id: 'import-file-input'
225
- }),
226
- React.createElement('label', {
227
- htmlFor: 'import-file-input',
228
- style: {
229
- display: 'inline-block',
230
- padding: '8px 16px',
231
- backgroundColor: isImporting ? '#dcdce4' : '#4945ff', // Same color as Export
232
- color: 'white',
233
- border: 'none',
234
- borderRadius: '4px',
235
- fontSize: '14px',
236
- fontWeight: '500',
237
- cursor: isImporting ? 'not-allowed' : 'pointer',
238
- transition: 'background-color 0.2s'
239
- }
240
- }, isImporting ? 'Importing...' : 'Import')
241
- )
242
- );
243
- };
244
-
245
- 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;