payload-wordpress-migrator 0.0.22

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.
Files changed (162) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +586 -0
  3. package/dist/components/BeforeDashboardClient.d.ts +14 -0
  4. package/dist/components/BeforeDashboardClient.js +225 -0
  5. package/dist/components/BeforeDashboardClient.js.map +1 -0
  6. package/dist/components/BeforeDashboardClient.module.css +175 -0
  7. package/dist/components/BeforeDashboardServer.d.ts +1 -0
  8. package/dist/components/BeforeDashboardServer.js +29 -0
  9. package/dist/components/BeforeDashboardServer.js.map +1 -0
  10. package/dist/components/ContentTypeSelect.d.ts +4 -0
  11. package/dist/components/ContentTypeSelect.js +147 -0
  12. package/dist/components/ContentTypeSelect.js.map +1 -0
  13. package/dist/components/FieldMappingConfiguration.d.ts +5 -0
  14. package/dist/components/FieldMappingConfiguration.js +361 -0
  15. package/dist/components/FieldMappingConfiguration.js.map +1 -0
  16. package/dist/components/FieldMappingConfiguration.module.css +75 -0
  17. package/dist/components/MigrationDashboardClient.d.ts +6 -0
  18. package/dist/components/MigrationDashboardClient.js +49 -0
  19. package/dist/components/MigrationDashboardClient.js.map +1 -0
  20. package/dist/components/MigrationDashboardClient.module.css +749 -0
  21. package/dist/components/SimpleFieldMapping.d.ts +5 -0
  22. package/dist/components/SimpleFieldMapping.js +437 -0
  23. package/dist/components/SimpleFieldMapping.js.map +1 -0
  24. package/dist/components/dashboard/JobActionButtons.d.ts +8 -0
  25. package/dist/components/dashboard/JobActionButtons.js +91 -0
  26. package/dist/components/dashboard/JobActionButtons.js.map +1 -0
  27. package/dist/components/dashboard/JobsTable.d.ts +6 -0
  28. package/dist/components/dashboard/JobsTable.js +86 -0
  29. package/dist/components/dashboard/JobsTable.js.map +1 -0
  30. package/dist/components/dashboard/LogViewer.d.ts +3 -0
  31. package/dist/components/dashboard/LogViewer.js +35 -0
  32. package/dist/components/dashboard/LogViewer.js.map +1 -0
  33. package/dist/components/dashboard/SiteConfigPanel.d.ts +12 -0
  34. package/dist/components/dashboard/SiteConfigPanel.js +205 -0
  35. package/dist/components/dashboard/SiteConfigPanel.js.map +1 -0
  36. package/dist/components/dashboard/StatsOverview.d.ts +5 -0
  37. package/dist/components/dashboard/StatsOverview.js +72 -0
  38. package/dist/components/dashboard/StatsOverview.js.map +1 -0
  39. package/dist/components/dashboard/index.d.ts +7 -0
  40. package/dist/components/dashboard/index.js +7 -0
  41. package/dist/components/dashboard/index.js.map +1 -0
  42. package/dist/components/dashboard/types.d.ts +46 -0
  43. package/dist/components/dashboard/types.js +2 -0
  44. package/dist/components/dashboard/types.js.map +1 -0
  45. package/dist/components/dashboard/useMigrationDashboard.d.ts +15 -0
  46. package/dist/components/dashboard/useMigrationDashboard.js +584 -0
  47. package/dist/components/dashboard/useMigrationDashboard.js.map +1 -0
  48. package/dist/exports/client.d.ts +4 -0
  49. package/dist/exports/client.js +5 -0
  50. package/dist/exports/client.js.map +1 -0
  51. package/dist/exports/rsc.d.ts +1 -0
  52. package/dist/exports/rsc.js +2 -0
  53. package/dist/exports/rsc.js.map +1 -0
  54. package/dist/index.d.ts +101 -0
  55. package/dist/index.js +443 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/utils/content/blocks.d.ts +6 -0
  58. package/dist/utils/content/blocks.js +93 -0
  59. package/dist/utils/content/blocks.js.map +1 -0
  60. package/dist/utils/content/fieldMapping.d.ts +9 -0
  61. package/dist/utils/content/fieldMapping.js +218 -0
  62. package/dist/utils/content/fieldMapping.js.map +1 -0
  63. package/dist/utils/content/index.d.ts +4 -0
  64. package/dist/utils/content/index.js +4 -0
  65. package/dist/utils/content/index.js.map +1 -0
  66. package/dist/utils/content/transformer.d.ts +5 -0
  67. package/dist/utils/content/transformer.js +323 -0
  68. package/dist/utils/content/transformer.js.map +1 -0
  69. package/dist/utils/endpoints/handlers.d.ts +9 -0
  70. package/dist/utils/endpoints/handlers.js +201 -0
  71. package/dist/utils/endpoints/handlers.js.map +1 -0
  72. package/dist/utils/endpoints/index.d.ts +2 -0
  73. package/dist/utils/endpoints/index.js +2 -0
  74. package/dist/utils/endpoints/index.js.map +1 -0
  75. package/dist/utils/fields/analyzer.d.ts +7 -0
  76. package/dist/utils/fields/analyzer.js +502 -0
  77. package/dist/utils/fields/analyzer.js.map +1 -0
  78. package/dist/utils/fields/index.d.ts +2 -0
  79. package/dist/utils/fields/index.js +2 -0
  80. package/dist/utils/fields/index.js.map +1 -0
  81. package/dist/utils/helpers/auth.d.ts +9 -0
  82. package/dist/utils/helpers/auth.js +50 -0
  83. package/dist/utils/helpers/auth.js.map +1 -0
  84. package/dist/utils/helpers/cache.d.ts +11 -0
  85. package/dist/utils/helpers/cache.js +47 -0
  86. package/dist/utils/helpers/cache.js.map +1 -0
  87. package/dist/utils/helpers/concurrency.d.ts +2 -0
  88. package/dist/utils/helpers/concurrency.js +26 -0
  89. package/dist/utils/helpers/concurrency.js.map +1 -0
  90. package/dist/utils/helpers/index.d.ts +8 -0
  91. package/dist/utils/helpers/index.js +8 -0
  92. package/dist/utils/helpers/index.js.map +1 -0
  93. package/dist/utils/helpers/objectHelpers.d.ts +3 -0
  94. package/dist/utils/helpers/objectHelpers.js +22 -0
  95. package/dist/utils/helpers/objectHelpers.js.map +1 -0
  96. package/dist/utils/helpers/rateLimiter.d.ts +10 -0
  97. package/dist/utils/helpers/rateLimiter.js +29 -0
  98. package/dist/utils/helpers/rateLimiter.js.map +1 -0
  99. package/dist/utils/helpers/responses.d.ts +3 -0
  100. package/dist/utils/helpers/responses.js +23 -0
  101. package/dist/utils/helpers/responses.js.map +1 -0
  102. package/dist/utils/helpers/wpHelpers.d.ts +6 -0
  103. package/dist/utils/helpers/wpHelpers.js +29 -0
  104. package/dist/utils/helpers/wpHelpers.js.map +1 -0
  105. package/dist/utils/lexical/constants.d.ts +37 -0
  106. package/dist/utils/lexical/constants.js +58 -0
  107. package/dist/utils/lexical/constants.js.map +1 -0
  108. package/dist/utils/lexical/htmlParser.d.ts +20 -0
  109. package/dist/utils/lexical/htmlParser.js +253 -0
  110. package/dist/utils/lexical/htmlParser.js.map +1 -0
  111. package/dist/utils/lexical/htmlToLexicalConverter.d.ts +55 -0
  112. package/dist/utils/lexical/htmlToLexicalConverter.js +999 -0
  113. package/dist/utils/lexical/htmlToLexicalConverter.js.map +1 -0
  114. package/dist/utils/lexical/index.d.ts +5 -0
  115. package/dist/utils/lexical/index.js +4 -0
  116. package/dist/utils/lexical/index.js.map +1 -0
  117. package/dist/utils/lexical/nodeFactories.d.ts +21 -0
  118. package/dist/utils/lexical/nodeFactories.js +91 -0
  119. package/dist/utils/lexical/nodeFactories.js.map +1 -0
  120. package/dist/utils/lexical/preprocessor.d.ts +4 -0
  121. package/dist/utils/lexical/preprocessor.js +302 -0
  122. package/dist/utils/lexical/preprocessor.js.map +1 -0
  123. package/dist/utils/media/download.d.ts +7 -0
  124. package/dist/utils/media/download.js +85 -0
  125. package/dist/utils/media/download.js.map +1 -0
  126. package/dist/utils/media/extraction.d.ts +12 -0
  127. package/dist/utils/media/extraction.js +58 -0
  128. package/dist/utils/media/extraction.js.map +1 -0
  129. package/dist/utils/media/import.d.ts +7 -0
  130. package/dist/utils/media/import.js +146 -0
  131. package/dist/utils/media/import.js.map +1 -0
  132. package/dist/utils/media/index.d.ts +6 -0
  133. package/dist/utils/media/index.js +6 -0
  134. package/dist/utils/media/index.js.map +1 -0
  135. package/dist/utils/media/upload.d.ts +4 -0
  136. package/dist/utils/media/upload.js +46 -0
  137. package/dist/utils/media/upload.js.map +1 -0
  138. package/dist/utils/media/validation.d.ts +8 -0
  139. package/dist/utils/media/validation.js +60 -0
  140. package/dist/utils/media/validation.js.map +1 -0
  141. package/dist/utils/migration/index.d.ts +3 -0
  142. package/dist/utils/migration/index.js +3 -0
  143. package/dist/utils/migration/index.js.map +1 -0
  144. package/dist/utils/migration/jobCrud.d.ts +4 -0
  145. package/dist/utils/migration/jobCrud.js +380 -0
  146. package/dist/utils/migration/jobCrud.js.map +1 -0
  147. package/dist/utils/migration/orchestrator.d.ts +5 -0
  148. package/dist/utils/migration/orchestrator.js +756 -0
  149. package/dist/utils/migration/orchestrator.js.map +1 -0
  150. package/dist/utils/types.d.ts +201 -0
  151. package/dist/utils/types.js +14 -0
  152. package/dist/utils/types.js.map +1 -0
  153. package/dist/utils/wordpress/client.d.ts +61 -0
  154. package/dist/utils/wordpress/client.js +365 -0
  155. package/dist/utils/wordpress/client.js.map +1 -0
  156. package/dist/utils/wordpress/index.d.ts +2 -0
  157. package/dist/utils/wordpress/index.js +2 -0
  158. package/dist/utils/wordpress/index.js.map +1 -0
  159. package/dist/utils/wordpressApi.d.ts +11 -0
  160. package/dist/utils/wordpressApi.js +25 -0
  161. package/dist/utils/wordpressApi.js.map +1 -0
  162. package/package.json +155 -0
@@ -0,0 +1,584 @@
1
+ "use client";
2
+ import { useState, useEffect } from 'react';
3
+
4
+ // Persist config without sensitive credentials (password never stored in localStorage)
5
+ const persistConfig = (config)=>{
6
+ const { wpPassword, ...safeConfig } = config;
7
+ localStorage.setItem('wp-site-config', JSON.stringify(safeConfig));
8
+ };
9
+ const useMigrationDashboard = (initialSummary)=>{
10
+ const [summary, setSummary] = useState(initialSummary);
11
+ const [loading, setLoading] = useState(false);
12
+ const [logs, setLogs] = useState([]);
13
+ const [siteConfig, setSiteConfig] = useState({
14
+ connectionStatus: 'not-scanned',
15
+ discoveredContent: [],
16
+ siteName: '',
17
+ totalItems: 0,
18
+ wpPassword: '',
19
+ wpSiteUrl: '',
20
+ wpUsername: ''
21
+ });
22
+ const [configState, setConfigState] = useState({
23
+ isEditing: true,
24
+ isSaved: false
25
+ });
26
+ const [showSiteConfig, setShowSiteConfig] = useState(true);
27
+ const addLog = (message)=>{
28
+ const timestamp = new Date().toLocaleTimeString();
29
+ const logEntry = `[${timestamp}] ${message}`;
30
+ setLogs((prev)=>[
31
+ ...prev,
32
+ logEntry
33
+ ]);
34
+ };
35
+ const clearLogs = ()=>{
36
+ setLogs([]);
37
+ };
38
+ const refreshDashboard = async ()=>{
39
+ try {
40
+ console.trace('Call stack:');
41
+ const response = await fetch('/api/wordpress/migration-summary?refresh=true');
42
+ const newSummary = await response.json();
43
+ setSummary(newSummary);
44
+ } catch (error) {
45
+ console.error('Failed to refresh dashboard:', error);
46
+ }
47
+ };
48
+ // Load initial data on mount
49
+ useEffect(()=>{
50
+ const loadInitialData = async ()=>{
51
+ await refreshDashboard();
52
+ };
53
+ const loadSavedConfig = ()=>{
54
+ try {
55
+ const savedConfig = localStorage.getItem('wp-site-config');
56
+ if (savedConfig) {
57
+ const parsed = JSON.parse(savedConfig);
58
+ setSiteConfig(parsed);
59
+ setConfigState({
60
+ isEditing: false,
61
+ isSaved: true
62
+ });
63
+ }
64
+ } catch (error) {
65
+ console.error('Failed to load saved config:', error);
66
+ }
67
+ };
68
+ void loadInitialData();
69
+ loadSavedConfig();
70
+ }, []);
71
+ // Smart periodic refresh - only when jobs are active
72
+ useEffect(()=>{
73
+ let refreshInterval = null;
74
+ if (summary.activeJobs > 0) {
75
+ refreshInterval = setInterval(()=>{
76
+ void refreshDashboard();
77
+ }, 15000);
78
+ }
79
+ return ()=>{
80
+ if (refreshInterval) {
81
+ clearInterval(refreshInterval);
82
+ }
83
+ };
84
+ }, [
85
+ summary.activeJobs
86
+ ]);
87
+ // Auto-close site config when scan is successful AND config is saved
88
+ useEffect(()=>{
89
+ if (siteConfig.connectionStatus === 'scanned' && configState.isSaved && !configState.isEditing && siteConfig.discoveredContent && siteConfig.discoveredContent.length > 0) {
90
+ const timer = setTimeout(()=>{
91
+ setShowSiteConfig(false);
92
+ }, 5000);
93
+ return ()=>clearTimeout(timer);
94
+ }
95
+ }, [
96
+ siteConfig.connectionStatus,
97
+ configState.isSaved,
98
+ configState.isEditing,
99
+ siteConfig.discoveredContent
100
+ ]);
101
+ // Only refresh on significant page visibility changes
102
+ useEffect(()=>{
103
+ let lastVisibilityChange = Date.now();
104
+ const handleVisibilityChange = ()=>{
105
+ if (!document.hidden && Date.now() - lastVisibilityChange > 30000) {
106
+ void refreshDashboard();
107
+ }
108
+ if (document.hidden) {
109
+ lastVisibilityChange = Date.now();
110
+ }
111
+ };
112
+ document.addEventListener('visibilitychange', handleVisibilityChange);
113
+ return ()=>{
114
+ document.removeEventListener('visibilitychange', handleVisibilityChange);
115
+ };
116
+ }, []);
117
+ // Listen for localStorage events to immediately refresh when jobs are changed
118
+ useEffect(()=>{
119
+ const handleStorageChange = (e)=>{
120
+ if (e.key === 'migration-event' && e.newValue) {
121
+ try {
122
+ const event = JSON.parse(e.newValue);
123
+ if (event.type?.startsWith('migration-job-')) {
124
+ void refreshDashboard();
125
+ }
126
+ } catch (error) {
127
+ // Ignore parsing errors
128
+ }
129
+ }
130
+ };
131
+ window.addEventListener('storage', handleStorageChange);
132
+ return ()=>window.removeEventListener('storage', handleStorageChange);
133
+ }, []);
134
+ const validateSiteConfig = ()=>{
135
+ const { siteName, wpPassword, wpSiteUrl, wpUsername } = siteConfig;
136
+ if (!siteName.trim() || !wpSiteUrl.trim() || !wpUsername.trim() || !wpPassword.trim()) {
137
+ alert('Please fill in all WordPress site configuration fields');
138
+ return false;
139
+ }
140
+ return true;
141
+ };
142
+ const requireSiteConfig = (actionVerb)=>{
143
+ if (!configState.isSaved || !validateSiteConfig() || siteConfig.connectionStatus !== 'scanned') {
144
+ alert(`Please save a valid WordPress site configuration and scan for content before ${actionVerb} migration jobs.`);
145
+ return false;
146
+ }
147
+ return true;
148
+ };
149
+ const getSiteCredentials = ()=>({
150
+ siteName: siteConfig.siteName,
151
+ wpPassword: siteConfig.wpPassword,
152
+ wpSiteUrl: siteConfig.wpSiteUrl,
153
+ wpUsername: siteConfig.wpUsername
154
+ });
155
+ const getJobName = (jobId)=>{
156
+ const job = summary.recentJobs?.find((j)=>j.id === jobId);
157
+ return job?.jobName || `Job ${jobId}`;
158
+ };
159
+ // --- Job action handlers ---
160
+ const startMigrationJob = async (jobId)=>{
161
+ try {
162
+ setLoading(true);
163
+ if (!requireSiteConfig('starting')) return;
164
+ await refreshDashboard();
165
+ clearLogs();
166
+ const jobName = getJobName(jobId);
167
+ addLog(`Migration "${jobName}" started`);
168
+ const response = await fetch(`/api/wordpress/migration-jobs`, {
169
+ body: JSON.stringify({
170
+ action: 'start',
171
+ jobId,
172
+ siteConfig: getSiteCredentials()
173
+ }),
174
+ headers: {
175
+ 'Content-Type': 'application/json'
176
+ },
177
+ method: 'POST'
178
+ });
179
+ if (!response.ok) {
180
+ const errorData = await response.json();
181
+ addLog(`Error starting migration "${jobName}": ${errorData.error || 'Unknown error'}`);
182
+ throw new Error(errorData.error || 'Failed to start job');
183
+ }
184
+ await refreshDashboard();
185
+ } catch (error) {
186
+ console.error('Error starting migration:', error);
187
+ alert(`Failed to start migration: ${error instanceof Error ? error.message : 'Unknown error'}`);
188
+ } finally{
189
+ setLoading(false);
190
+ }
191
+ };
192
+ const pauseMigrationJob = async (jobId)=>{
193
+ try {
194
+ const jobName = getJobName(jobId);
195
+ addLog(`Migration "${jobName}" paused`);
196
+ const response = await fetch(`/api/wordpress/migration-jobs?jobId=${jobId}&action=pause`, {
197
+ method: 'PUT'
198
+ });
199
+ if (!response.ok) {
200
+ addLog(`Error pausing migration "${jobName}"`);
201
+ throw new Error('Failed to pause migration job');
202
+ }
203
+ await refreshDashboard();
204
+ } catch (error) {
205
+ console.error('Error pausing migration:', error);
206
+ alert('Failed to pause migration job');
207
+ }
208
+ };
209
+ const resumeMigrationJob = async (jobId)=>{
210
+ try {
211
+ setLoading(true);
212
+ if (!requireSiteConfig('resuming')) return;
213
+ const jobName = getJobName(jobId);
214
+ addLog(`Migration "${jobName}" resumed`);
215
+ const response = await fetch(`/api/wordpress/migration-jobs?jobId=${jobId}&action=resume`, {
216
+ method: 'PUT'
217
+ });
218
+ if (!response.ok) {
219
+ const errorData = await response.json();
220
+ addLog(`Error resuming migration "${jobName}": ${errorData.error || 'Unknown error'}`);
221
+ throw new Error(errorData.error || 'Failed to resume job');
222
+ }
223
+ const startResponse = await fetch(`/api/wordpress/migration-jobs`, {
224
+ body: JSON.stringify({
225
+ action: 'resume',
226
+ jobId,
227
+ siteConfig: getSiteCredentials()
228
+ }),
229
+ headers: {
230
+ 'Content-Type': 'application/json'
231
+ },
232
+ method: 'POST'
233
+ });
234
+ if (!startResponse.ok) {
235
+ const errorData = await startResponse.json();
236
+ addLog(`Error resuming migration "${jobName}": ${errorData.error || 'Unknown error'}`);
237
+ throw new Error(errorData.error || 'Failed to resume job');
238
+ }
239
+ await refreshDashboard();
240
+ } catch (error) {
241
+ console.error('Error resuming migration:', error);
242
+ alert(`Failed to resume migration: ${error instanceof Error ? error.message : 'Unknown error'}`);
243
+ } finally{
244
+ setLoading(false);
245
+ }
246
+ };
247
+ const retryMigrationJob = async (jobId)=>{
248
+ try {
249
+ setLoading(true);
250
+ if (!requireSiteConfig('retrying')) return;
251
+ const jobName = getJobName(jobId);
252
+ addLog(`Retrying migration "${jobName}"`);
253
+ const response = await fetch(`/api/wordpress/migration-jobs`, {
254
+ body: JSON.stringify({
255
+ action: 'retry',
256
+ jobId,
257
+ siteConfig: getSiteCredentials()
258
+ }),
259
+ headers: {
260
+ 'Content-Type': 'application/json'
261
+ },
262
+ method: 'POST'
263
+ });
264
+ if (!response.ok) {
265
+ const errorData = await response.json();
266
+ addLog(`Error retrying migration "${jobName}": ${errorData.error || 'Unknown error'}`);
267
+ throw new Error(errorData.error || 'Failed to retry job');
268
+ }
269
+ await refreshDashboard();
270
+ } catch (error) {
271
+ console.error('Error retrying migration:', error);
272
+ alert(`Failed to retry migration: ${error instanceof Error ? error.message : 'Unknown error'}`);
273
+ } finally{
274
+ setLoading(false);
275
+ }
276
+ };
277
+ const updateMigrationJob = async (jobId)=>{
278
+ try {
279
+ setLoading(true);
280
+ if (!requireSiteConfig('updating')) return;
281
+ const jobName = getJobName(jobId);
282
+ const confirmed = confirm(`Are you sure you want to update the "${jobName}" migration? This will update existing items with new field mappings without re-processing all content.`);
283
+ if (!confirmed) {
284
+ return;
285
+ }
286
+ addLog(`Updating migration "${jobName}" with new field mappings`);
287
+ const response = await fetch(`/api/wordpress/migration-jobs`, {
288
+ body: JSON.stringify({
289
+ action: 'update',
290
+ jobId,
291
+ siteConfig: getSiteCredentials()
292
+ }),
293
+ headers: {
294
+ 'Content-Type': 'application/json'
295
+ },
296
+ method: 'POST'
297
+ });
298
+ if (!response.ok) {
299
+ const errorData = await response.json();
300
+ addLog(`Error updating migration "${jobName}": ${errorData.error || 'Unknown error'}`);
301
+ throw new Error(errorData.error || 'Failed to update job');
302
+ }
303
+ await response.json();
304
+ addLog(`Successfully started update for "${jobName}"`);
305
+ await refreshDashboard();
306
+ } catch (error) {
307
+ console.error('Error updating migration:', error);
308
+ alert(`Failed to update migration job: ${error instanceof Error ? error.message : 'Unknown error'}`);
309
+ } finally{
310
+ setLoading(false);
311
+ }
312
+ };
313
+ const restartMigrationJob = async (jobId)=>{
314
+ try {
315
+ setLoading(true);
316
+ if (!requireSiteConfig('restarting')) return;
317
+ const jobName = getJobName(jobId);
318
+ const confirmed = confirm(`Are you sure you want to restart the "${jobName}" migration? This will re-process ALL items, including those that were already successfully migrated.`);
319
+ if (!confirmed) {
320
+ return;
321
+ }
322
+ addLog(`Restarting migration "${jobName}"`);
323
+ const response = await fetch(`/api/wordpress/migration-jobs`, {
324
+ body: JSON.stringify({
325
+ action: 'restart',
326
+ jobId,
327
+ siteConfig: getSiteCredentials()
328
+ }),
329
+ headers: {
330
+ 'Content-Type': 'application/json'
331
+ },
332
+ method: 'POST'
333
+ });
334
+ if (!response.ok) {
335
+ const errorData = await response.json();
336
+ addLog(`Error restarting migration "${jobName}": ${errorData.error || 'Unknown error'}`);
337
+ throw new Error(errorData.error || 'Failed to restart job');
338
+ }
339
+ await refreshDashboard();
340
+ } catch (error) {
341
+ console.error('Error restarting migration:', error);
342
+ alert(`Failed to restart migration: ${error instanceof Error ? error.message : 'Unknown error'}`);
343
+ } finally{
344
+ setLoading(false);
345
+ }
346
+ };
347
+ const rollbackMigrationJob = async (jobId)=>{
348
+ try {
349
+ setLoading(true);
350
+ const jobName = getJobName(jobId);
351
+ const confirmed = confirm(`Are you sure you want to rollback the "${jobName}" migration? This will DELETE all items that were migrated by this job.`);
352
+ if (!confirmed) {
353
+ return;
354
+ }
355
+ addLog(`Rolling back migration "${jobName}"`);
356
+ const response = await fetch(`/api/wordpress/migration-jobs`, {
357
+ body: JSON.stringify({
358
+ action: 'rollback',
359
+ jobId
360
+ }),
361
+ headers: {
362
+ 'Content-Type': 'application/json'
363
+ },
364
+ method: 'POST'
365
+ });
366
+ if (!response.ok) {
367
+ const errorData = await response.json();
368
+ addLog(`Error rolling back migration "${jobName}": ${errorData.error || 'Unknown error'}`);
369
+ throw new Error(errorData.error || 'Failed to rollback job');
370
+ }
371
+ const result = await response.json();
372
+ addLog(`Rollback complete for "${jobName}": ${result.data?.deleted ?? 0} items deleted`);
373
+ await refreshDashboard();
374
+ } catch (error) {
375
+ console.error('Error rolling back migration:', error);
376
+ alert(`Failed to rollback migration: ${error instanceof Error ? error.message : 'Unknown error'}`);
377
+ } finally{
378
+ setLoading(false);
379
+ }
380
+ };
381
+ const handleJobAction = async (action, jobId)=>{
382
+ switch(action){
383
+ case 'start':
384
+ return startMigrationJob(jobId);
385
+ case 'pause':
386
+ return pauseMigrationJob(jobId);
387
+ case 'resume':
388
+ return resumeMigrationJob(jobId);
389
+ case 'retry':
390
+ return retryMigrationJob(jobId);
391
+ case 'update':
392
+ return updateMigrationJob(jobId);
393
+ case 'restart':
394
+ return restartMigrationJob(jobId);
395
+ case 'rollback':
396
+ return rollbackMigrationJob(jobId);
397
+ }
398
+ };
399
+ // --- Config handlers ---
400
+ const scanWordPressContent = async ()=>{
401
+ if (!validateSiteConfig()) {
402
+ return;
403
+ }
404
+ setSiteConfig((prev)=>({
405
+ ...prev,
406
+ connectionStatus: 'scanning'
407
+ }));
408
+ try {
409
+ const response = await fetch('/api/wordpress/discover-content', {
410
+ body: JSON.stringify({
411
+ wpPassword: siteConfig.wpPassword,
412
+ wpSiteUrl: siteConfig.wpSiteUrl,
413
+ wpUsername: siteConfig.wpUsername
414
+ }),
415
+ headers: {
416
+ 'Content-Type': 'application/json'
417
+ },
418
+ method: 'POST'
419
+ });
420
+ const result = await response.json();
421
+ if (response.ok && result.success) {
422
+ const contentTypes = result.contentTypes || [];
423
+ const totalItems = result.totalItems || 0;
424
+ const updatedConfig = {
425
+ ...siteConfig,
426
+ connectionStatus: 'scanned',
427
+ discoveredContent: contentTypes,
428
+ totalItems
429
+ };
430
+ setSiteConfig(updatedConfig);
431
+ try {
432
+ persistConfig(updatedConfig);
433
+ } catch (error) {
434
+ console.error('Failed to save discovered content to localStorage:', error);
435
+ }
436
+ if (contentTypes.length === 0) {
437
+ alert('WordPress scan completed, but no content was found. This could be due to:\n\n' + '• Empty WordPress site (no posts, pages, media, etc.)\n' + '• Insufficient user permissions\n' + '• WordPress REST API restrictions\n\n' + 'Please check your WordPress site and user permissions.');
438
+ }
439
+ } else {
440
+ const updatedConfig = {
441
+ ...siteConfig,
442
+ connectionStatus: 'failed',
443
+ discoveredContent: [],
444
+ totalItems: 0
445
+ };
446
+ setSiteConfig(updatedConfig);
447
+ try {
448
+ persistConfig(updatedConfig);
449
+ } catch (error) {
450
+ console.error('Failed to save failed scan state to localStorage:', error);
451
+ }
452
+ alert(`WordPress scan failed: ${result.error || 'Unknown error'}`);
453
+ }
454
+ } catch (error) {
455
+ console.error('Content scan failed:', error);
456
+ const updatedConfig = {
457
+ ...siteConfig,
458
+ connectionStatus: 'failed',
459
+ discoveredContent: [],
460
+ totalItems: 0
461
+ };
462
+ setSiteConfig(updatedConfig);
463
+ try {
464
+ persistConfig(updatedConfig);
465
+ } catch (storageError) {
466
+ console.error('Failed to save network error state to localStorage:', storageError);
467
+ }
468
+ alert('WordPress scan failed: Network error');
469
+ }
470
+ };
471
+ const handleSiteConfigChange = (field, value)=>{
472
+ const connectionFields = [
473
+ 'wpSiteUrl',
474
+ 'wpUsername',
475
+ 'wpPassword'
476
+ ];
477
+ const shouldReset = connectionFields.includes(field);
478
+ setSiteConfig((prev)=>({
479
+ ...prev,
480
+ [field]: value,
481
+ ...shouldReset && {
482
+ connectionStatus: 'not-scanned',
483
+ discoveredContent: [],
484
+ totalItems: 0
485
+ }
486
+ }));
487
+ };
488
+ const handleSaveConfig = async ()=>{
489
+ if (!validateSiteConfig()) {
490
+ return;
491
+ }
492
+ try {
493
+ persistConfig(siteConfig);
494
+ setConfigState({
495
+ isEditing: false,
496
+ isSaved: true
497
+ });
498
+ await scanWordPressContent();
499
+ } catch (error) {
500
+ console.error('Failed to save configuration:', error);
501
+ alert('Failed to save configuration. Please try again.');
502
+ }
503
+ };
504
+ const handleEditConfig = ()=>{
505
+ setConfigState({
506
+ isEditing: true,
507
+ isSaved: true
508
+ });
509
+ };
510
+ const handleCancelEdit = ()=>{
511
+ try {
512
+ const savedConfig = localStorage.getItem('wp-site-config');
513
+ if (savedConfig) {
514
+ const parsed = JSON.parse(savedConfig);
515
+ setSiteConfig((prev)=>({
516
+ ...prev,
517
+ ...parsed,
518
+ connectionStatus: parsed.connectionStatus || prev.connectionStatus || 'not-scanned',
519
+ discoveredContent: parsed.discoveredContent || prev.discoveredContent || [],
520
+ totalItems: parsed.totalItems || prev.totalItems || 0
521
+ }));
522
+ }
523
+ setConfigState({
524
+ isEditing: false,
525
+ isSaved: true
526
+ });
527
+ } catch (error) {
528
+ console.error('Failed to reload saved configuration:', error);
529
+ }
530
+ };
531
+ // Combine client-side action logs with server-side migration logs
532
+ const getAllLogs = ()=>{
533
+ const combinedLogs = [];
534
+ logs.forEach((log)=>{
535
+ const timestampMatch = log.match(/^\[(\d{1,2}:\d{2}:\d{2}(?:\s?[AP]M)?)\]/);
536
+ let timestamp = new Date();
537
+ if (timestampMatch) {
538
+ const timeStr = timestampMatch[1];
539
+ const today = new Date();
540
+ const timeWithDate = `${today.toDateString()} ${timeStr}`;
541
+ timestamp = new Date(timeWithDate);
542
+ }
543
+ combinedLogs.push({
544
+ message: log,
545
+ source: 'client',
546
+ timestamp
547
+ });
548
+ });
549
+ if (summary.recentLogs) {
550
+ summary.recentLogs.forEach((log)=>{
551
+ const timestamp = new Date(log.timestamp);
552
+ const levelIcon = log.level === 'error' ? '❌' : log.level === 'warning' ? '⚠️' : 'ℹ️';
553
+ const formattedMessage = `[${timestamp.toLocaleTimeString()}] ${levelIcon} [${log.jobName}] ${log.message}`;
554
+ combinedLogs.push({
555
+ level: log.level,
556
+ message: formattedMessage,
557
+ source: 'server',
558
+ timestamp
559
+ });
560
+ });
561
+ }
562
+ return combinedLogs.sort((a, b)=>b.timestamp.getTime() - a.timestamp.getTime()).slice(0, 100).map((log)=>log.message);
563
+ };
564
+ return {
565
+ configState,
566
+ loading,
567
+ showSiteConfig,
568
+ siteConfig,
569
+ summary,
570
+ // Config actions
571
+ handleCancelEdit,
572
+ handleEditConfig,
573
+ handleSaveConfig,
574
+ handleSiteConfigChange,
575
+ setShowSiteConfig,
576
+ // Job actions
577
+ handleJobAction,
578
+ // Utilities
579
+ getAllLogs
580
+ };
581
+ };
582
+
583
+ export { useMigrationDashboard };
584
+ //# sourceMappingURL=useMigrationDashboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useMigrationDashboard.js","sources":["../../../src/components/dashboard/useMigrationDashboard.ts"],"sourcesContent":["'use client'\n\nimport { useEffect, useState } from 'react'\n\nimport type { JobActionType, MigrationSummary, SiteConfigState, WordPressSiteConfig } from './types'\n\n// Persist config without sensitive credentials (password never stored in localStorage)\nconst persistConfig = (config: WordPressSiteConfig) => {\n const { wpPassword, ...safeConfig } = config\n localStorage.setItem('wp-site-config', JSON.stringify(safeConfig))\n}\n\nexport const useMigrationDashboard = (initialSummary: MigrationSummary) => {\n const [summary, setSummary] = useState<MigrationSummary>(initialSummary)\n const [loading, setLoading] = useState(false)\n const [logs, setLogs] = useState<string[]>([])\n\n const [siteConfig, setSiteConfig] = useState<WordPressSiteConfig>({\n connectionStatus: 'not-scanned',\n discoveredContent: [],\n siteName: '',\n totalItems: 0,\n wpPassword: '',\n wpSiteUrl: '',\n wpUsername: '',\n })\n const [configState, setConfigState] = useState<SiteConfigState>({\n isEditing: true,\n isSaved: false,\n })\n const [showSiteConfig, setShowSiteConfig] = useState(true)\n\n const addLog = (message: string) => {\n const timestamp = new Date().toLocaleTimeString()\n const logEntry = `[${timestamp}] ${message}`\n setLogs((prev) => [...prev, logEntry])\n }\n\n const clearLogs = () => {\n setLogs([])\n }\n\n const refreshDashboard = async () => {\n try {\n console.trace('Call stack:')\n const response = await fetch('/api/wordpress/migration-summary?refresh=true')\n const newSummary = await response.json()\n setSummary(newSummary)\n } catch (error) {\n console.error('Failed to refresh dashboard:', error)\n }\n }\n\n // Load initial data on mount\n useEffect(() => {\n const loadInitialData = async () => {\n await refreshDashboard()\n }\n\n const loadSavedConfig = () => {\n try {\n const savedConfig = localStorage.getItem('wp-site-config')\n if (savedConfig) {\n const parsed = JSON.parse(savedConfig)\n setSiteConfig(parsed)\n setConfigState({ isEditing: false, isSaved: true })\n }\n } catch (error) {\n console.error('Failed to load saved config:', error)\n }\n }\n\n void loadInitialData()\n loadSavedConfig()\n }, [])\n\n // Smart periodic refresh - only when jobs are active\n useEffect(() => {\n let refreshInterval: NodeJS.Timeout | null = null\n\n if (summary.activeJobs > 0) {\n refreshInterval = setInterval(() => {\n void refreshDashboard()\n }, 15000)\n }\n\n return () => {\n if (refreshInterval) {\n clearInterval(refreshInterval)\n }\n }\n }, [summary.activeJobs])\n\n // Auto-close site config when scan is successful AND config is saved\n useEffect(() => {\n if (\n siteConfig.connectionStatus === 'scanned' &&\n configState.isSaved &&\n !configState.isEditing &&\n siteConfig.discoveredContent &&\n siteConfig.discoveredContent.length > 0\n ) {\n const timer = setTimeout(() => {\n setShowSiteConfig(false)\n }, 5000)\n\n return () => clearTimeout(timer)\n }\n }, [\n siteConfig.connectionStatus,\n configState.isSaved,\n configState.isEditing,\n siteConfig.discoveredContent,\n ])\n\n // Only refresh on significant page visibility changes\n useEffect(() => {\n let lastVisibilityChange = Date.now()\n\n const handleVisibilityChange = () => {\n if (!document.hidden && Date.now() - lastVisibilityChange > 30000) {\n void refreshDashboard()\n }\n\n if (document.hidden) {\n lastVisibilityChange = Date.now()\n }\n }\n\n document.addEventListener('visibilitychange', handleVisibilityChange)\n\n return () => {\n document.removeEventListener('visibilitychange', handleVisibilityChange)\n }\n }, [])\n\n // Listen for localStorage events to immediately refresh when jobs are changed\n useEffect(() => {\n const handleStorageChange = (e: StorageEvent) => {\n if (e.key === 'migration-event' && e.newValue) {\n try {\n const event = JSON.parse(e.newValue)\n if (event.type?.startsWith('migration-job-')) {\n void refreshDashboard()\n }\n } catch (error) {\n // Ignore parsing errors\n }\n }\n }\n\n window.addEventListener('storage', handleStorageChange)\n return () => window.removeEventListener('storage', handleStorageChange)\n }, [])\n\n const validateSiteConfig = () => {\n const { siteName, wpPassword, wpSiteUrl, wpUsername } = siteConfig\n if (!siteName.trim() || !wpSiteUrl.trim() || !wpUsername.trim() || !wpPassword.trim()) {\n alert('Please fill in all WordPress site configuration fields')\n return false\n }\n return true\n }\n\n const requireSiteConfig = (actionVerb: string): boolean => {\n if (\n !configState.isSaved ||\n !validateSiteConfig() ||\n siteConfig.connectionStatus !== 'scanned'\n ) {\n alert(\n `Please save a valid WordPress site configuration and scan for content before ${actionVerb} migration jobs.`,\n )\n return false\n }\n return true\n }\n\n const getSiteCredentials = () => ({\n siteName: siteConfig.siteName,\n wpPassword: siteConfig.wpPassword,\n wpSiteUrl: siteConfig.wpSiteUrl,\n wpUsername: siteConfig.wpUsername,\n })\n\n const getJobName = (jobId: string) => {\n const job = summary.recentJobs?.find((j) => j.id === jobId)\n return job?.jobName || `Job ${jobId}`\n }\n\n // --- Job action handlers ---\n\n const startMigrationJob = async (jobId: string) => {\n try {\n setLoading(true)\n if (!requireSiteConfig('starting')) return\n\n await refreshDashboard()\n clearLogs()\n\n const jobName = getJobName(jobId)\n addLog(`Migration \"${jobName}\" started`)\n\n const response = await fetch(`/api/wordpress/migration-jobs`, {\n body: JSON.stringify({\n action: 'start',\n jobId,\n siteConfig: getSiteCredentials(),\n }),\n headers: { 'Content-Type': 'application/json' },\n method: 'POST',\n })\n\n if (!response.ok) {\n const errorData = await response.json()\n addLog(`Error starting migration \"${jobName}\": ${errorData.error || 'Unknown error'}`)\n throw new Error(errorData.error || 'Failed to start job')\n }\n\n await refreshDashboard()\n } catch (error) {\n console.error('Error starting migration:', error)\n alert(\n `Failed to start migration: ${error instanceof Error ? error.message : 'Unknown error'}`,\n )\n } finally {\n setLoading(false)\n }\n }\n\n const pauseMigrationJob = async (jobId: string) => {\n try {\n const jobName = getJobName(jobId)\n addLog(`Migration \"${jobName}\" paused`)\n\n const response = await fetch(`/api/wordpress/migration-jobs?jobId=${jobId}&action=pause`, {\n method: 'PUT',\n })\n\n if (!response.ok) {\n addLog(`Error pausing migration \"${jobName}\"`)\n throw new Error('Failed to pause migration job')\n }\n\n await refreshDashboard()\n } catch (error) {\n console.error('Error pausing migration:', error)\n alert('Failed to pause migration job')\n }\n }\n\n const resumeMigrationJob = async (jobId: string) => {\n try {\n setLoading(true)\n if (!requireSiteConfig('resuming')) return\n\n const jobName = getJobName(jobId)\n addLog(`Migration \"${jobName}\" resumed`)\n\n const response = await fetch(`/api/wordpress/migration-jobs?jobId=${jobId}&action=resume`, {\n method: 'PUT',\n })\n\n if (!response.ok) {\n const errorData = await response.json()\n addLog(`Error resuming migration \"${jobName}\": ${errorData.error || 'Unknown error'}`)\n throw new Error(errorData.error || 'Failed to resume job')\n }\n\n const startResponse = await fetch(`/api/wordpress/migration-jobs`, {\n body: JSON.stringify({\n action: 'resume',\n jobId,\n siteConfig: getSiteCredentials(),\n }),\n headers: { 'Content-Type': 'application/json' },\n method: 'POST',\n })\n\n if (!startResponse.ok) {\n const errorData = await startResponse.json()\n addLog(`Error resuming migration \"${jobName}\": ${errorData.error || 'Unknown error'}`)\n throw new Error(errorData.error || 'Failed to resume job')\n }\n\n await refreshDashboard()\n } catch (error) {\n console.error('Error resuming migration:', error)\n alert(\n `Failed to resume migration: ${error instanceof Error ? error.message : 'Unknown error'}`,\n )\n } finally {\n setLoading(false)\n }\n }\n\n const retryMigrationJob = async (jobId: string) => {\n try {\n setLoading(true)\n if (!requireSiteConfig('retrying')) return\n\n const jobName = getJobName(jobId)\n addLog(`Retrying migration \"${jobName}\"`)\n\n const response = await fetch(`/api/wordpress/migration-jobs`, {\n body: JSON.stringify({\n action: 'retry',\n jobId,\n siteConfig: getSiteCredentials(),\n }),\n headers: { 'Content-Type': 'application/json' },\n method: 'POST',\n })\n\n if (!response.ok) {\n const errorData = await response.json()\n addLog(`Error retrying migration \"${jobName}\": ${errorData.error || 'Unknown error'}`)\n throw new Error(errorData.error || 'Failed to retry job')\n }\n\n await refreshDashboard()\n } catch (error) {\n console.error('Error retrying migration:', error)\n alert(\n `Failed to retry migration: ${error instanceof Error ? error.message : 'Unknown error'}`,\n )\n } finally {\n setLoading(false)\n }\n }\n\n const updateMigrationJob = async (jobId: string) => {\n try {\n setLoading(true)\n if (!requireSiteConfig('updating')) return\n\n const jobName = getJobName(jobId)\n\n const confirmed = confirm(\n `Are you sure you want to update the \"${jobName}\" migration? This will update existing items with new field mappings without re-processing all content.`,\n )\n\n if (!confirmed) {\n return\n }\n\n addLog(`Updating migration \"${jobName}\" with new field mappings`)\n\n const response = await fetch(`/api/wordpress/migration-jobs`, {\n body: JSON.stringify({\n action: 'update',\n jobId,\n siteConfig: getSiteCredentials(),\n }),\n headers: { 'Content-Type': 'application/json' },\n method: 'POST',\n })\n\n if (!response.ok) {\n const errorData = await response.json()\n addLog(`Error updating migration \"${jobName}\": ${errorData.error || 'Unknown error'}`)\n throw new Error(errorData.error || 'Failed to update job')\n }\n\n await response.json()\n addLog(`Successfully started update for \"${jobName}\"`)\n\n await refreshDashboard()\n } catch (error) {\n console.error('Error updating migration:', error)\n alert(\n `Failed to update migration job: ${error instanceof Error ? error.message : 'Unknown error'}`,\n )\n } finally {\n setLoading(false)\n }\n }\n\n const restartMigrationJob = async (jobId: string) => {\n try {\n setLoading(true)\n if (!requireSiteConfig('restarting')) return\n\n const jobName = getJobName(jobId)\n\n const confirmed = confirm(\n `Are you sure you want to restart the \"${jobName}\" migration? This will re-process ALL items, including those that were already successfully migrated.`,\n )\n\n if (!confirmed) {\n return\n }\n\n addLog(`Restarting migration \"${jobName}\"`)\n\n const response = await fetch(`/api/wordpress/migration-jobs`, {\n body: JSON.stringify({\n action: 'restart',\n jobId,\n siteConfig: getSiteCredentials(),\n }),\n headers: { 'Content-Type': 'application/json' },\n method: 'POST',\n })\n\n if (!response.ok) {\n const errorData = await response.json()\n addLog(`Error restarting migration \"${jobName}\": ${errorData.error || 'Unknown error'}`)\n throw new Error(errorData.error || 'Failed to restart job')\n }\n\n await refreshDashboard()\n } catch (error) {\n console.error('Error restarting migration:', error)\n alert(\n `Failed to restart migration: ${error instanceof Error ? error.message : 'Unknown error'}`,\n )\n } finally {\n setLoading(false)\n }\n }\n\n const rollbackMigrationJob = async (jobId: string) => {\n try {\n setLoading(true)\n\n const jobName = getJobName(jobId)\n\n const confirmed = confirm(\n `Are you sure you want to rollback the \"${jobName}\" migration? This will DELETE all items that were migrated by this job.`,\n )\n\n if (!confirmed) {\n return\n }\n\n addLog(`Rolling back migration \"${jobName}\"`)\n\n const response = await fetch(`/api/wordpress/migration-jobs`, {\n body: JSON.stringify({\n action: 'rollback',\n jobId,\n }),\n headers: { 'Content-Type': 'application/json' },\n method: 'POST',\n })\n\n if (!response.ok) {\n const errorData = await response.json()\n addLog(`Error rolling back migration \"${jobName}\": ${errorData.error || 'Unknown error'}`)\n throw new Error(errorData.error || 'Failed to rollback job')\n }\n\n const result = await response.json()\n addLog(`Rollback complete for \"${jobName}\": ${result.data?.deleted ?? 0} items deleted`)\n\n await refreshDashboard()\n } catch (error) {\n console.error('Error rolling back migration:', error)\n alert(\n `Failed to rollback migration: ${error instanceof Error ? error.message : 'Unknown error'}`,\n )\n } finally {\n setLoading(false)\n }\n }\n\n const handleJobAction = async (action: JobActionType, jobId: string) => {\n switch (action) {\n case 'start':\n return startMigrationJob(jobId)\n case 'pause':\n return pauseMigrationJob(jobId)\n case 'resume':\n return resumeMigrationJob(jobId)\n case 'retry':\n return retryMigrationJob(jobId)\n case 'update':\n return updateMigrationJob(jobId)\n case 'restart':\n return restartMigrationJob(jobId)\n case 'rollback':\n return rollbackMigrationJob(jobId)\n }\n }\n\n // --- Config handlers ---\n\n const scanWordPressContent = async () => {\n if (!validateSiteConfig()) {\n return\n }\n\n setSiteConfig((prev) => ({ ...prev, connectionStatus: 'scanning' }))\n\n try {\n const response = await fetch('/api/wordpress/discover-content', {\n body: JSON.stringify({\n wpPassword: siteConfig.wpPassword,\n wpSiteUrl: siteConfig.wpSiteUrl,\n wpUsername: siteConfig.wpUsername,\n }),\n headers: { 'Content-Type': 'application/json' },\n method: 'POST',\n })\n\n const result = await response.json()\n\n if (response.ok && result.success) {\n const contentTypes = result.contentTypes || []\n const totalItems = result.totalItems || 0\n\n const updatedConfig = {\n ...siteConfig,\n connectionStatus: 'scanned' as const,\n discoveredContent: contentTypes,\n totalItems,\n }\n\n setSiteConfig(updatedConfig)\n\n try {\n persistConfig(updatedConfig)\n } catch (error) {\n console.error('Failed to save discovered content to localStorage:', error)\n }\n\n if (contentTypes.length === 0) {\n alert(\n 'WordPress scan completed, but no content was found. This could be due to:\\n\\n' +\n '• Empty WordPress site (no posts, pages, media, etc.)\\n' +\n '• Insufficient user permissions\\n' +\n '• WordPress REST API restrictions\\n\\n' +\n 'Please check your WordPress site and user permissions.',\n )\n }\n } else {\n const updatedConfig = {\n ...siteConfig,\n connectionStatus: 'failed' as const,\n discoveredContent: [],\n totalItems: 0,\n }\n\n setSiteConfig(updatedConfig)\n\n try {\n persistConfig(updatedConfig)\n } catch (error) {\n console.error('Failed to save failed scan state to localStorage:', error)\n }\n\n alert(`WordPress scan failed: ${result.error || 'Unknown error'}`)\n }\n } catch (error) {\n console.error('Content scan failed:', error)\n\n const updatedConfig = {\n ...siteConfig,\n connectionStatus: 'failed' as const,\n discoveredContent: [],\n totalItems: 0,\n }\n\n setSiteConfig(updatedConfig)\n\n try {\n persistConfig(updatedConfig)\n } catch (storageError) {\n console.error('Failed to save network error state to localStorage:', storageError)\n }\n\n alert('WordPress scan failed: Network error')\n }\n }\n\n const handleSiteConfigChange = (field: keyof WordPressSiteConfig, value: string) => {\n const connectionFields = ['wpSiteUrl', 'wpUsername', 'wpPassword']\n const shouldReset = connectionFields.includes(field)\n\n setSiteConfig((prev) => ({\n ...prev,\n [field]: value,\n ...(shouldReset && {\n connectionStatus: 'not-scanned',\n discoveredContent: [],\n totalItems: 0,\n }),\n }))\n }\n\n const handleSaveConfig = async () => {\n if (!validateSiteConfig()) {\n return\n }\n\n try {\n persistConfig(siteConfig)\n setConfigState({\n isEditing: false,\n isSaved: true,\n })\n\n await scanWordPressContent()\n } catch (error) {\n console.error('Failed to save configuration:', error)\n alert('Failed to save configuration. Please try again.')\n }\n }\n\n const handleEditConfig = () => {\n setConfigState({\n isEditing: true,\n isSaved: true,\n })\n }\n\n const handleCancelEdit = () => {\n try {\n const savedConfig = localStorage.getItem('wp-site-config')\n if (savedConfig) {\n const parsed = JSON.parse(savedConfig)\n setSiteConfig((prev) => ({\n ...prev,\n ...parsed,\n connectionStatus: parsed.connectionStatus || prev.connectionStatus || 'not-scanned',\n discoveredContent: parsed.discoveredContent || prev.discoveredContent || [],\n totalItems: parsed.totalItems || prev.totalItems || 0,\n }))\n }\n setConfigState({\n isEditing: false,\n isSaved: true,\n })\n } catch (error) {\n console.error('Failed to reload saved configuration:', error)\n }\n }\n\n // Combine client-side action logs with server-side migration logs\n const getAllLogs = (): string[] => {\n const combinedLogs: Array<{\n level?: string\n message: string\n source: 'client' | 'server'\n timestamp: Date\n }> = []\n\n logs.forEach((log) => {\n const timestampMatch = log.match(/^\\[(\\d{1,2}:\\d{2}:\\d{2}(?:\\s?[AP]M)?)\\]/)\n let timestamp = new Date()\n\n if (timestampMatch) {\n const timeStr = timestampMatch[1]\n const today = new Date()\n const timeWithDate = `${today.toDateString()} ${timeStr}`\n timestamp = new Date(timeWithDate)\n }\n\n combinedLogs.push({\n message: log,\n source: 'client',\n timestamp,\n })\n })\n\n if (summary.recentLogs) {\n summary.recentLogs.forEach((log) => {\n const timestamp = new Date(log.timestamp)\n const levelIcon = log.level === 'error' ? '❌' : log.level === 'warning' ? '⚠️' : 'ℹ️'\n const formattedMessage = `[${timestamp.toLocaleTimeString()}] ${levelIcon} [${log.jobName}] ${log.message}`\n\n combinedLogs.push({\n level: log.level,\n message: formattedMessage,\n source: 'server',\n timestamp,\n })\n })\n }\n\n return combinedLogs\n .sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime())\n .slice(0, 100)\n .map((log) => log.message)\n }\n\n return {\n configState,\n loading,\n showSiteConfig,\n siteConfig,\n summary,\n\n // Config actions\n handleCancelEdit,\n handleEditConfig,\n handleSaveConfig,\n handleSiteConfigChange,\n setShowSiteConfig,\n\n // Job actions\n handleJobAction,\n\n // Utilities\n getAllLogs,\n }\n}\n"],"names":["localStorage","discoveredContent","setLogs","prev","logEntry","console","loadSavedConfig","refreshInterval","summary","siteConfig","configState","lastVisibilityChange","alert","siteName","wpPassword","wpSiteUrl","wpUsername","clearLogs","addLog","jobId","totalItems","connectionStatus","timestamp","combinedLogs","level","loading","showSiteConfig","handleCancelEdit","handleEditConfig","handleSaveConfig","handleSiteConfigChange","setShowSiteConfig","handleJobAction","getAllLogs"],"mappings":";;;AAMA;AACA;AACE;AACAA;AACF;AAEO;AACL;AACA;AACA;AAEA;;AAEEC;;;;;;AAMF;AACA;;;AAGA;AACA;AAEA;;AAEE;AACAC;AAAsBC;AAAMC;AAAS;AACvC;AAEA;AACEF;AACF;AAEA;;AAEIG;;;;AAIF;;AAEA;AACF;;;AAIE;;AAEA;AAEA;;;AAGI;;;;;;AAGmD;AACnD;AACF;;AAEA;AACF;;AAGAC;AACF;;;AAIE;;AAGEC;;;AAGF;;AAGE;;AAEA;AACF;;AACEC;AAAmB;;;AAIrB;AAOE;;;AAIA;AACF;;AAEAC;AACAC;AACAA;AACAD;AACD;;;;AAMC;;;AAGE;;AAGEE;AACF;AACF;;;;AAMA;AACF;;;AAIE;AACE;;AAEI;AACA;;AAEA;AACF;;AAEA;AACF;AACF;;AAGA;AACF;AAEA;;AAEE;;;AAGA;;AAEF;AAEA;;AAMIC;;AAIF;;AAEF;;AAGEC;AACAC;AACAC;AACAC;;AAGF;;AAEE;AACF;;AAIA;;;;;AAMIC;AAEA;AACAC;AAEA;;;AAGIC;;AAEF;;;AAC8C;;AAEhD;;;;AAKE;AACF;;AAGF;;;;;AAOA;AACF;AAEA;;AAEI;AACAD;;;AAIA;;AAGEA;AACA;AACF;;AAGF;;;AAGA;AACF;AAEA;;;;AAKI;AACAA;;;AAIA;;;;AAKE;AACF;AAEA;;;AAGIC;;AAEF;;;AAC8C;;AAEhD;;;;AAKE;AACF;;AAGF;;;;;AAOA;AACF;AAEA;;;;AAKI;AACAD;AAEA;;;AAGIC;;AAEF;;;AAC8C;;AAEhD;;;;AAKE;AACF;;AAGF;;;;;AAOA;AACF;AAEA;;;;AAKI;AAEA;AAIA;AACE;AACF;AAEAD;AAEA;;;AAGIC;;AAEF;;;AAC8C;;AAEhD;;;;AAKE;AACF;AAEA;AACAD;;AAGF;;;;;AAOA;AACF;AAEA;;;;AAKI;AAEA;AAIA;AACE;AACF;AAEAA;AAEA;;;AAGIC;;AAEF;;;AAC8C;;AAEhD;;;;AAKE;AACF;;AAGF;;;;;AAOA;AACF;AAEA;;;AAII;AAEA;AAIA;AACE;AACF;AAEAD;AAEA;;;AAGIC;AACF;;;AAC8C;;AAEhD;;;;AAKE;AACF;;AAGAD;;AAGF;;;;;AAOA;AACF;;;;AAKM;;AAEA;;AAEA;;AAEA;;AAEA;;AAEA;;AAEA;AACJ;AACF;;AAIA;AACE;AACE;AACF;;AAE2B;;;;;;AAKrBJ;AACAC;AACAC;AACF;;;AAC8C;;AAEhD;;AAIA;AACE;;AAGA;AACE;;;AAGAI;AACF;;;;AAMA;;AAEA;;;AAUA;;AAEA;AACE;;AAEAnB;;AAEF;;;;AAMA;;AAEA;AAEAW;AACF;AACF;;AAGE;AACE;;AAEAX;;AAEF;;;;AAMA;;AAEA;;AAGF;AACF;;AAGE;AAA0B;AAAa;AAAc;AAAa;;;AAIhE;AACA;AACA;;AAEEA;;;;AAIN;AAEA;AACE;AACE;AACF;;;;;;AAOE;;AAGF;;;AAGA;AACF;AAEA;;;;AAIE;AACF;AAEA;;;AAGI;;;AAGI;AACA;AACAoB;AACApB;AACAmB;;AAEJ;;;;AAIA;AACF;;AAEA;AACF;;AAGA;AACE;;;AASE;AAEA;;AAEE;AACA;AACAE;AACF;AAEAC;;;AAGED;AACF;AACF;;AAGEd;AACE;;AAEA;AAEAe;AACEC;;;AAGAF;AACF;AACF;AACF;;AAMF;;AAGEZ;AACAe;AACAC;AACAjB;AACAD;;AAGAmB;AACAC;AACAC;AACAC;AACAC;;AAGAC;;AAGAC;AACF;AACF;;"}
@@ -0,0 +1,4 @@
1
+ export { default as ContentTypeSelect } from '../components/ContentTypeSelect.js';
2
+ export { default as FieldMappingConfiguration } from '../components/FieldMappingConfiguration.js';
3
+ export { MigrationDashboardClient } from '../components/MigrationDashboardClient.js';
4
+ export { default as SimpleFieldMapping } from '../components/SimpleFieldMapping.js';
@@ -0,0 +1,5 @@
1
+ export { default as ContentTypeSelect } from '../components/ContentTypeSelect.js';
2
+ export { default as FieldMappingConfiguration } from '../components/FieldMappingConfiguration.js';
3
+ export { MigrationDashboardClient } from '../components/MigrationDashboardClient.js';
4
+ export { default as SimpleFieldMapping } from '../components/SimpleFieldMapping.js';
5
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
@@ -0,0 +1 @@
1
+ export { MigrationDashboardServer } from '../components/BeforeDashboardServer.js';
@@ -0,0 +1,2 @@
1
+ export { MigrationDashboardServer } from '../components/BeforeDashboardServer.js';
2
+ //# sourceMappingURL=rsc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rsc.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}