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,361 @@
1
+ "use client";
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import { useField } from '@payloadcms/ui';
4
+ import { useState, useEffect, useCallback } from 'react';
5
+ import styles from './FieldMappingConfiguration.module.css';
6
+
7
+ const FieldMappingConfiguration = ({ path })=>{
8
+ const { setValue, value } = useField({
9
+ path
10
+ });
11
+ const { value: contentTypeValue } = useField({
12
+ path: 'contentType'
13
+ });
14
+ const { value: targetCollectionValue } = useField({
15
+ path: 'targetCollection'
16
+ });
17
+ const [wpFields, setWpFields] = useState([]);
18
+ const [payloadFields, setPayloadFields] = useState([]);
19
+ const [fieldMappings, setFieldMappings] = useState([]);
20
+ const [loading, setLoading] = useState(false);
21
+ const [error, setError] = useState('');
22
+ // Parse existing field mappings from JSON value
23
+ useEffect(()=>{
24
+ if (value) {
25
+ try {
26
+ const parsed = typeof value === 'string' ? JSON.parse(value) : value;
27
+ if (parsed && Array.isArray(parsed.fieldMappings)) {
28
+ setFieldMappings(parsed.fieldMappings);
29
+ }
30
+ } catch (error) {
31
+ console.error('Failed to parse existing field mappings:', error);
32
+ }
33
+ }
34
+ }, [
35
+ value
36
+ ]);
37
+ // Flatten nested fields with dot notation paths
38
+ const flattenFields = (fields, prefix = '')=>{
39
+ const flattened = [];
40
+ fields.forEach((field)=>{
41
+ const fieldPath = prefix ? `${prefix}.${field.name}` : field.name;
42
+ flattened.push({
43
+ ...field,
44
+ label: field.label || field.name,
45
+ path: fieldPath
46
+ });
47
+ // Handle nested fields
48
+ if (field.fields && Array.isArray(field.fields)) {
49
+ flattened.push(...flattenFields(field.fields, fieldPath));
50
+ }
51
+ // Handle array fields with nested schemas
52
+ if (field.type === 'array' && field.of && Array.isArray(field.of)) {
53
+ field.of.forEach((arrayField, index)=>{
54
+ if (arrayField.fields) {
55
+ flattened.push(...flattenFields(arrayField.fields, `${fieldPath}[${index}]`));
56
+ }
57
+ });
58
+ }
59
+ // Handle group fields
60
+ if (field.type === 'group' && field.fields) {
61
+ flattened.push(...flattenFields(field.fields, fieldPath));
62
+ }
63
+ });
64
+ return flattened;
65
+ };
66
+ // Fetch WordPress content type fields
67
+ const fetchWordPressFields = useCallback(async (contentType)=>{
68
+ try {
69
+ setLoading(true);
70
+ setError('');
71
+ const savedConfig = localStorage.getItem('wp-site-config');
72
+ if (!savedConfig) {
73
+ throw new Error('WordPress site configuration not found');
74
+ }
75
+ const config = JSON.parse(savedConfig);
76
+ const response = await fetch('/api/wordpress/content-fields', {
77
+ body: JSON.stringify({
78
+ contentType,
79
+ wpPassword: config.wpPassword,
80
+ wpSiteUrl: config.wpSiteUrl,
81
+ wpUsername: config.wpUsername
82
+ }),
83
+ headers: {
84
+ 'Content-Type': 'application/json'
85
+ },
86
+ method: 'POST'
87
+ });
88
+ const result = await response.json();
89
+ if (response.ok && result.success) {
90
+ const flattened = flattenFields(result.fields || []);
91
+ setWpFields(flattened);
92
+ } else {
93
+ throw new Error(result.error || 'Failed to fetch WordPress fields');
94
+ }
95
+ } catch (error) {
96
+ console.error('Error fetching WordPress fields:', error);
97
+ setError(error instanceof Error ? error.message : 'Failed to fetch WordPress fields');
98
+ setWpFields([]);
99
+ } finally{
100
+ setLoading(false);
101
+ }
102
+ }, []) // Empty dependency array since the function doesn't depend on any props or state
103
+ ;
104
+ // Fetch Payload collection fields
105
+ const fetchPayloadFields = useCallback(async (collectionSlug)=>{
106
+ try {
107
+ const response = await fetch(`/api/collections/${collectionSlug}/fields`);
108
+ const result = await response.json();
109
+ if (response.ok) {
110
+ const flattened = flattenFields(result.fields || []);
111
+ setPayloadFields(flattened);
112
+ } else {
113
+ throw new Error('Failed to fetch Payload collection fields');
114
+ }
115
+ } catch (error) {
116
+ console.error('Error fetching Payload fields:', error);
117
+ setPayloadFields([]);
118
+ }
119
+ }, []) // Empty dependency array since the function doesn't depend on any props or state
120
+ ;
121
+ // Load fields when both content type and target collection are selected
122
+ useEffect(()=>{
123
+ if (contentTypeValue && targetCollectionValue) {
124
+ void fetchWordPressFields(contentTypeValue);
125
+ void fetchPayloadFields(targetCollectionValue);
126
+ } else {
127
+ setWpFields([]);
128
+ setPayloadFields([]);
129
+ setFieldMappings([]);
130
+ }
131
+ }, [
132
+ contentTypeValue,
133
+ targetCollectionValue,
134
+ fetchWordPressFields,
135
+ fetchPayloadFields
136
+ ]);
137
+ // Add new field mapping
138
+ const addFieldMapping = ()=>{
139
+ const newMapping = {
140
+ payloadField: '',
141
+ wpField: ''
142
+ };
143
+ const updatedMappings = [
144
+ ...fieldMappings,
145
+ newMapping
146
+ ];
147
+ setFieldMappings(updatedMappings);
148
+ updateValue(updatedMappings);
149
+ };
150
+ // Remove field mapping
151
+ const removeFieldMapping = (index)=>{
152
+ const updatedMappings = fieldMappings.filter((_, i)=>i !== index);
153
+ setFieldMappings(updatedMappings);
154
+ updateValue(updatedMappings);
155
+ };
156
+ // Update field mapping
157
+ const updateFieldMapping = (index, field, value)=>{
158
+ const updatedMappings = fieldMappings.map((mapping, i)=>i === index ? {
159
+ ...mapping,
160
+ [field]: value
161
+ } : mapping);
162
+ setFieldMappings(updatedMappings);
163
+ updateValue(updatedMappings);
164
+ };
165
+ // Update the form value
166
+ const updateValue = (mappings)=>{
167
+ const mappingConfig = {
168
+ contentType: contentTypeValue,
169
+ fieldMappings: mappings,
170
+ targetCollection: targetCollectionValue
171
+ };
172
+ setValue(JSON.stringify(mappingConfig, null, 2));
173
+ };
174
+ if (!contentTypeValue || !targetCollectionValue) {
175
+ return /*#__PURE__*/ jsxs("div", {
176
+ className: "field-type",
177
+ children: [
178
+ /*#__PURE__*/ jsx("div", {
179
+ className: "field-label",
180
+ children: /*#__PURE__*/ jsx("label", {
181
+ children: "Field Mapping Configuration"
182
+ })
183
+ }),
184
+ /*#__PURE__*/ jsx("div", {
185
+ className: "field-description",
186
+ children: 'Please select both "Content Type to Migrate" and "Target Payload Collection" to configure field mappings.'
187
+ })
188
+ ]
189
+ });
190
+ }
191
+ return /*#__PURE__*/ jsxs("div", {
192
+ className: "field-type",
193
+ children: [
194
+ /*#__PURE__*/ jsx("div", {
195
+ className: "field-label",
196
+ children: /*#__PURE__*/ jsx("label", {
197
+ children: "Field Mapping Configuration"
198
+ })
199
+ }),
200
+ loading && /*#__PURE__*/ jsx("div", {
201
+ className: "field-description",
202
+ children: "Loading field schemas..."
203
+ }),
204
+ error && /*#__PURE__*/ jsx("div", {
205
+ className: "field-error",
206
+ children: /*#__PURE__*/ jsx("div", {
207
+ className: "field-error__message",
208
+ children: error
209
+ })
210
+ }),
211
+ !loading && !error && (wpFields.length > 0 || payloadFields.length > 0) && /*#__PURE__*/ jsxs("div", {
212
+ className: styles.fieldMappingContainer,
213
+ children: [
214
+ /*#__PURE__*/ jsx("div", {
215
+ className: styles.fieldMappingHeader,
216
+ children: /*#__PURE__*/ jsxs("div", {
217
+ className: styles.fieldMappingColumns,
218
+ children: [
219
+ /*#__PURE__*/ jsxs("div", {
220
+ className: styles.fieldMappingColumn,
221
+ children: [
222
+ /*#__PURE__*/ jsxs("h4", {
223
+ children: [
224
+ "WordPress Fields (",
225
+ String(contentTypeValue || ''),
226
+ ")"
227
+ ]
228
+ }),
229
+ /*#__PURE__*/ jsx("div", {
230
+ className: styles.fieldList,
231
+ children: wpFields.map((field)=>/*#__PURE__*/ jsxs("div", {
232
+ className: styles.fieldItem,
233
+ children: [
234
+ /*#__PURE__*/ jsx("strong", {
235
+ children: field.path
236
+ }),
237
+ /*#__PURE__*/ jsx("span", {
238
+ className: styles.fieldTypeBadge,
239
+ children: field.type
240
+ })
241
+ ]
242
+ }, field.path))
243
+ })
244
+ ]
245
+ }),
246
+ /*#__PURE__*/ jsxs("div", {
247
+ className: styles.fieldMappingColumn,
248
+ children: [
249
+ /*#__PURE__*/ jsxs("h4", {
250
+ children: [
251
+ "Payload Fields (",
252
+ String(targetCollectionValue || ''),
253
+ ")"
254
+ ]
255
+ }),
256
+ /*#__PURE__*/ jsx("div", {
257
+ className: styles.fieldList,
258
+ children: payloadFields.map((field)=>/*#__PURE__*/ jsxs("div", {
259
+ className: styles.fieldItem,
260
+ children: [
261
+ /*#__PURE__*/ jsx("strong", {
262
+ children: field.path
263
+ }),
264
+ /*#__PURE__*/ jsx("span", {
265
+ className: styles.fieldTypeBadge,
266
+ children: field.type
267
+ })
268
+ ]
269
+ }, field.path))
270
+ })
271
+ ]
272
+ })
273
+ ]
274
+ })
275
+ }),
276
+ /*#__PURE__*/ jsxs("div", {
277
+ className: styles.fieldMappingMappings,
278
+ children: [
279
+ /*#__PURE__*/ jsxs("div", {
280
+ className: styles.fieldMappingHeader,
281
+ children: [
282
+ /*#__PURE__*/ jsx("h4", {
283
+ children: "Field Mappings"
284
+ }),
285
+ /*#__PURE__*/ jsx("button", {
286
+ className: "btn btn--style-secondary btn--size-small",
287
+ onClick: addFieldMapping,
288
+ type: "button",
289
+ children: "Add Mapping"
290
+ })
291
+ ]
292
+ }),
293
+ fieldMappings.map((mapping, index)=>/*#__PURE__*/ jsxs("div", {
294
+ className: styles.fieldMappingRow,
295
+ children: [
296
+ /*#__PURE__*/ jsxs("select", {
297
+ className: "select",
298
+ onChange: (e)=>updateFieldMapping(index, 'wpField', e.target.value),
299
+ value: mapping.wpField,
300
+ children: [
301
+ /*#__PURE__*/ jsx("option", {
302
+ value: "",
303
+ children: "Select WordPress field..."
304
+ }),
305
+ wpFields.map((field)=>/*#__PURE__*/ jsxs("option", {
306
+ value: field.path,
307
+ children: [
308
+ field.path,
309
+ " (",
310
+ field.type,
311
+ ")"
312
+ ]
313
+ }, field.path))
314
+ ]
315
+ }),
316
+ /*#__PURE__*/ jsx("span", {
317
+ className: styles.fieldMappingArrow,
318
+ children: "→"
319
+ }),
320
+ /*#__PURE__*/ jsxs("select", {
321
+ className: "select",
322
+ onChange: (e)=>updateFieldMapping(index, 'payloadField', e.target.value),
323
+ value: mapping.payloadField,
324
+ children: [
325
+ /*#__PURE__*/ jsx("option", {
326
+ value: "",
327
+ children: "Select Payload field..."
328
+ }),
329
+ payloadFields.map((field)=>/*#__PURE__*/ jsxs("option", {
330
+ value: field.path,
331
+ children: [
332
+ field.path,
333
+ " (",
334
+ field.type,
335
+ ")"
336
+ ]
337
+ }, field.path))
338
+ ]
339
+ }),
340
+ /*#__PURE__*/ jsx("button", {
341
+ className: "btn btn--style-error btn--size-small",
342
+ onClick: ()=>removeFieldMapping(index),
343
+ type: "button",
344
+ children: "Remove"
345
+ })
346
+ ]
347
+ }, index)),
348
+ fieldMappings.length === 0 && /*#__PURE__*/ jsx("div", {
349
+ className: styles.fieldDescription,
350
+ children: 'No field mappings configured. Click "Add Mapping" to start mapping fields.'
351
+ })
352
+ ]
353
+ })
354
+ ]
355
+ })
356
+ ]
357
+ });
358
+ };
359
+
360
+ export { FieldMappingConfiguration as default };
361
+ //# sourceMappingURL=FieldMappingConfiguration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FieldMappingConfiguration.js","sources":["../../src/components/FieldMappingConfiguration.tsx"],"sourcesContent":["'use client'\n\nimport { useField } from '@payloadcms/ui'\nimport React, { useCallback, useEffect, useState } from 'react'\n\nimport styles from './FieldMappingConfiguration.module.css'\n\ntype WordPressField = {\n label: string\n name: string\n nested?: WordPressField[]\n path: string // Full dot notation path like \"meta.custom_field\"\n type: string\n}\n\ntype PayloadField = {\n label: string\n name: string\n nested?: PayloadField[]\n path: string // Full dot notation path\n type: string\n}\n\ntype FieldMapping = {\n payloadField: string\n wpField: string\n}\n\nconst FieldMappingConfiguration: React.FC<{ path: string }> = ({ path }) => {\n const { setValue, value } = useField({ path })\n const { value: contentTypeValue } = useField({ path: 'contentType' })\n const { value: targetCollectionValue } = useField({ path: 'targetCollection' })\n\n const [wpFields, setWpFields] = useState<WordPressField[]>([])\n const [payloadFields, setPayloadFields] = useState<PayloadField[]>([])\n const [fieldMappings, setFieldMappings] = useState<FieldMapping[]>([])\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState('')\n\n // Parse existing field mappings from JSON value\n useEffect(() => {\n if (value) {\n try {\n const parsed = typeof value === 'string' ? JSON.parse(value) : value\n if (parsed && Array.isArray(parsed.fieldMappings)) {\n setFieldMappings(parsed.fieldMappings)\n }\n } catch (error) {\n console.error('Failed to parse existing field mappings:', error)\n }\n }\n }, [value])\n\n // Flatten nested fields with dot notation paths\n const flattenFields = (fields: any[], prefix = ''): any[] => {\n const flattened: any[] = []\n\n fields.forEach((field) => {\n const fieldPath = prefix ? `${prefix}.${field.name}` : field.name\n\n flattened.push({\n ...field,\n label: field.label || field.name,\n path: fieldPath,\n })\n\n // Handle nested fields\n if (field.fields && Array.isArray(field.fields)) {\n flattened.push(...flattenFields(field.fields, fieldPath))\n }\n\n // Handle array fields with nested schemas\n if (field.type === 'array' && field.of && Array.isArray(field.of)) {\n field.of.forEach((arrayField: any, index: number) => {\n if (arrayField.fields) {\n flattened.push(...flattenFields(arrayField.fields, `${fieldPath}[${index}]`))\n }\n })\n }\n\n // Handle group fields\n if (field.type === 'group' && field.fields) {\n flattened.push(...flattenFields(field.fields, fieldPath))\n }\n })\n\n return flattened\n }\n\n // Fetch WordPress content type fields\n const fetchWordPressFields = useCallback(async (contentType: string) => {\n try {\n setLoading(true)\n setError('')\n\n const savedConfig = localStorage.getItem('wp-site-config')\n if (!savedConfig) {\n throw new Error('WordPress site configuration not found')\n }\n\n const config = JSON.parse(savedConfig)\n\n const response = await fetch('/api/wordpress/content-fields', {\n body: JSON.stringify({\n contentType,\n wpPassword: config.wpPassword,\n wpSiteUrl: config.wpSiteUrl,\n wpUsername: config.wpUsername,\n }),\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n })\n\n const result = await response.json()\n\n if (response.ok && result.success) {\n const flattened = flattenFields(result.fields || [])\n setWpFields(flattened)\n } else {\n throw new Error(result.error || 'Failed to fetch WordPress fields')\n }\n } catch (error) {\n console.error('Error fetching WordPress fields:', error)\n setError(error instanceof Error ? error.message : 'Failed to fetch WordPress fields')\n setWpFields([])\n } finally {\n setLoading(false)\n }\n }, []) // Empty dependency array since the function doesn't depend on any props or state\n\n // Fetch Payload collection fields\n const fetchPayloadFields = useCallback(async (collectionSlug: string) => {\n try {\n const response = await fetch(`/api/collections/${collectionSlug}/fields`)\n const result = await response.json()\n\n if (response.ok) {\n const flattened = flattenFields(result.fields || [])\n setPayloadFields(flattened)\n } else {\n throw new Error('Failed to fetch Payload collection fields')\n }\n } catch (error) {\n console.error('Error fetching Payload fields:', error)\n setPayloadFields([])\n }\n }, []) // Empty dependency array since the function doesn't depend on any props or state\n\n // Load fields when both content type and target collection are selected\n useEffect(() => {\n if (contentTypeValue && targetCollectionValue) {\n void fetchWordPressFields(contentTypeValue as string)\n void fetchPayloadFields(targetCollectionValue as string)\n } else {\n setWpFields([])\n setPayloadFields([])\n setFieldMappings([])\n }\n }, [contentTypeValue, targetCollectionValue, fetchWordPressFields, fetchPayloadFields])\n\n // Add new field mapping\n const addFieldMapping = () => {\n const newMapping: FieldMapping = {\n payloadField: '',\n wpField: '',\n }\n const updatedMappings = [...fieldMappings, newMapping]\n setFieldMappings(updatedMappings)\n updateValue(updatedMappings)\n }\n\n // Remove field mapping\n const removeFieldMapping = (index: number) => {\n const updatedMappings = fieldMappings.filter((_, i) => i !== index)\n setFieldMappings(updatedMappings)\n updateValue(updatedMappings)\n }\n\n // Update field mapping\n const updateFieldMapping = (index: number, field: keyof FieldMapping, value: string) => {\n const updatedMappings = fieldMappings.map((mapping, i) =>\n i === index ? { ...mapping, [field]: value } : mapping,\n )\n setFieldMappings(updatedMappings)\n updateValue(updatedMappings)\n }\n\n // Update the form value\n const updateValue = (mappings: FieldMapping[]) => {\n const mappingConfig = {\n contentType: contentTypeValue,\n fieldMappings: mappings,\n targetCollection: targetCollectionValue,\n }\n setValue(JSON.stringify(mappingConfig, null, 2))\n }\n\n if (!contentTypeValue || !targetCollectionValue) {\n return (\n <div className=\"field-type\">\n <div className=\"field-label\">\n <label>Field Mapping Configuration</label>\n </div>\n <div className=\"field-description\">\n Please select both \"Content Type to Migrate\" and \"Target Payload Collection\" to configure\n field mappings.\n </div>\n </div>\n )\n }\n\n return (\n <div className=\"field-type\">\n <div className=\"field-label\">\n <label>Field Mapping Configuration</label>\n </div>\n\n {loading && <div className=\"field-description\">Loading field schemas...</div>}\n\n {error && (\n <div className=\"field-error\">\n <div className=\"field-error__message\">{error}</div>\n </div>\n )}\n\n {!loading && !error && (wpFields.length > 0 || payloadFields.length > 0) && (\n <div className={styles.fieldMappingContainer}>\n <div className={styles.fieldMappingHeader}>\n <div className={styles.fieldMappingColumns}>\n <div className={styles.fieldMappingColumn}>\n <h4>WordPress Fields ({String(contentTypeValue || '')})</h4>\n <div className={styles.fieldList}>\n {wpFields.map((field) => (\n <div className={styles.fieldItem} key={field.path}>\n <strong>{field.path}</strong>\n <span className={styles.fieldTypeBadge}>{field.type}</span>\n </div>\n ))}\n </div>\n </div>\n\n <div className={styles.fieldMappingColumn}>\n <h4>Payload Fields ({String(targetCollectionValue || '')})</h4>\n <div className={styles.fieldList}>\n {payloadFields.map((field) => (\n <div className={styles.fieldItem} key={field.path}>\n <strong>{field.path}</strong>\n <span className={styles.fieldTypeBadge}>{field.type}</span>\n </div>\n ))}\n </div>\n </div>\n </div>\n </div>\n\n <div className={styles.fieldMappingMappings}>\n <div className={styles.fieldMappingHeader}>\n <h4>Field Mappings</h4>\n <button\n className=\"btn btn--style-secondary btn--size-small\"\n onClick={addFieldMapping}\n type=\"button\"\n >\n Add Mapping\n </button>\n </div>\n\n {fieldMappings.map((mapping, index) => (\n <div className={styles.fieldMappingRow} key={index}>\n <select\n className=\"select\"\n onChange={(e) => updateFieldMapping(index, 'wpField', e.target.value)}\n value={mapping.wpField}\n >\n <option value=\"\">Select WordPress field...</option>\n {wpFields.map((field) => (\n <option key={field.path} value={field.path}>\n {field.path} ({field.type})\n </option>\n ))}\n </select>\n\n <span className={styles.fieldMappingArrow}>→</span>\n\n <select\n className=\"select\"\n onChange={(e) => updateFieldMapping(index, 'payloadField', e.target.value)}\n value={mapping.payloadField}\n >\n <option value=\"\">Select Payload field...</option>\n {payloadFields.map((field) => (\n <option key={field.path} value={field.path}>\n {field.path} ({field.type})\n </option>\n ))}\n </select>\n\n <button\n className=\"btn btn--style-error btn--size-small\"\n onClick={() => removeFieldMapping(index)}\n type=\"button\"\n >\n Remove\n </button>\n </div>\n ))}\n\n {fieldMappings.length === 0 && (\n <div className={styles.fieldDescription}>\n No field mappings configured. Click \"Add Mapping\" to start mapping fields.\n </div>\n )}\n </div>\n </div>\n )}\n </div>\n )\n}\n\nexport default FieldMappingConfiguration\n"],"names":["path","setFieldMappings","value","flattened","label","field","contentType","wpPassword","wpSiteUrl","wpUsername","setError","setWpFields","setPayloadFields","contentTypeValue","targetCollectionValue","fetchWordPressFields","fetchPayloadFields","fieldMappings","newMapping","setValue","loading","error","className","String","wpFields","payloadFields","onClick","index"],"mappings":";;;;;;AA4BA;AACE;AAAuCA;AAAK;AAC5C;;AAAmE;AACnE;;AAA6E;AAE7E;AACA;AACA;AACA;AACA;;;AAIE;;AAEI;AACA;AACEC;AACF;AACF;;AAEA;AACF;;AACEC;AAAM;;AAGV;AACE;;;AAKEC;AACE;AACAC;;AAEF;;;AAIED;AACF;;AAGA;AACEE;;AAEIF;AACF;AACF;AACF;;AAGA;AACEA;AACF;AACF;;AAGF;;;;;;;AASI;AACE;AACF;;;;AAMIG;AACAC;AACAC;AACAC;AACF;;;AAGA;;AAEF;;AAIA;AACE;;;AAGA;AACF;AACF;;AAEEC;AACAC;;;AAGF;;;;;;;;;AAUI;;;AAGA;AACF;AACF;;AAEEC;AACF;;;;;AAKA;AACE;AACA;;AAEAD;AACAC;AACAX;AACF;;AACEY;AAAkBC;AAAuBC;AAAsBC;AAAmB;;AAGtF;AACE;;;AAGA;AACA;AAA4BC;AAAeC;AAAW;;;AAGxD;;AAGA;AACE;;;AAGF;;;;AAKoB;AAAY;;;;AAIhC;;AAGA;AACE;;;;AAIA;AACAC;AACF;;AAGE;;;;;AAGM;AAAO;;;;;AAE0B;;;;AAMzC;AAEA;;;;;AAGM;AAAO;;;AAGRC;;AAA8C;;AAE9CC;;AAEG;;AAAuCA;;;AAI1C;AACMC;;;AACEA;AACH;AAAKA;;;AACEA;;;;AACC;AAAmBC;AAA+B;;;;AACjDD;AACFE;AACMF;;;AACMjB;;;AACHiB;AAAmCjB;;;AAFJA;;;;;AAQxCiB;;;;AACC;AAAiBC;AAAoC;;;;AACpDD;AACFG;AACMH;;;AACMjB;;;AACHiB;AAAmCjB;;;AAFJA;;;;;;;;AAU5CiB;;;AACEA;;;AACC;;;;;;AAKH;;;;AAKFL;AACMK;;;;;AAIDpB;;;;AAEiB;;AAChBsB;AAC0BtB;;AACtBG;AAAW;AAAGA;AAAW;;AADfA;;;;AAMXiB;AAAqC;;;;;AAKzCpB;;;;AAEiB;;AAChBuB;AAC0BvB;;AACtBG;AAAW;AAAGA;AAAW;;AADfA;;;;;AAQfqB;;AAED;;;AAjC0CC;;AAwCxCL;AAAoC;;;;;;;;AASvD;;"}
@@ -0,0 +1,75 @@
1
+ .fieldMappingContainer {
2
+ border: 1px solid var(--theme-elevation-100);
3
+ border-radius: 4px;
4
+ padding: 16px;
5
+ margin-top: 8px;
6
+ }
7
+
8
+ .fieldMappingColumns {
9
+ display: grid;
10
+ grid-template-columns: 1fr 1fr;
11
+ gap: 24px;
12
+ margin-bottom: 24px;
13
+ }
14
+
15
+ .fieldMappingColumn h4 {
16
+ margin: 0 0 12px 0;
17
+ padding-bottom: 8px;
18
+ border-bottom: 1px solid var(--theme-elevation-100);
19
+ }
20
+
21
+ .fieldList {
22
+ max-height: 200px;
23
+ overflow-y: auto;
24
+ border: 1px solid var(--theme-elevation-100);
25
+ border-radius: 4px;
26
+ padding: 8px;
27
+ }
28
+
29
+ .fieldItem {
30
+ display: flex;
31
+ justify-content: space-between;
32
+ align-items: center;
33
+ padding: 4px 8px;
34
+ margin: 2px 0;
35
+ background: var(--theme-elevation-50);
36
+ border-radius: 3px;
37
+ font-size: 13px;
38
+ }
39
+
40
+ .fieldTypeBadge {
41
+ background: var(--theme-elevation-200);
42
+ padding: 2px 6px;
43
+ border-radius: 12px;
44
+ font-size: 11px;
45
+ color: var(--theme-elevation-800);
46
+ }
47
+
48
+ .fieldMappingHeader {
49
+ display: flex;
50
+ justify-content: space-between;
51
+ align-items: center;
52
+ margin-bottom: 16px;
53
+ }
54
+
55
+ .fieldMappingHeader h4 {
56
+ margin: 0;
57
+ }
58
+
59
+ .fieldMappingRow {
60
+ display: grid;
61
+ grid-template-columns: 1fr auto 1fr auto;
62
+ gap: 12px;
63
+ align-items: center;
64
+ margin-bottom: 12px;
65
+ padding: 12px;
66
+ border: 1px solid var(--theme-elevation-100);
67
+ border-radius: 4px;
68
+ background: var(--theme-background);
69
+ }
70
+
71
+ .fieldMappingArrow {
72
+ font-size: 18px;
73
+ color: var(--theme-elevation-400);
74
+ font-weight: bold;
75
+ }
@@ -0,0 +1,6 @@
1
+ import type { MigrationSummary } from './dashboard/types';
2
+ type MigrationDashboardClientProps = {
3
+ summary: MigrationSummary;
4
+ };
5
+ export declare const MigrationDashboardClient: ({ summary: initialSummary, }: MigrationDashboardClientProps) => import("react/jsx-runtime").JSX.Element;
6
+ export {};
@@ -0,0 +1,49 @@
1
+ "use client";
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import { JobsTable } from './dashboard/JobsTable.js';
4
+ import { LogViewer } from './dashboard/LogViewer.js';
5
+ import { SiteConfigPanel } from './dashboard/SiteConfigPanel.js';
6
+ import { StatsOverview } from './dashboard/StatsOverview.js';
7
+ import { useMigrationDashboard } from './dashboard/useMigrationDashboard.js';
8
+ import styles from './MigrationDashboardClient.module.css';
9
+
10
+ const MigrationDashboardClient = ({ summary: initialSummary })=>{
11
+ const { configState, loading, showSiteConfig, siteConfig, summary, handleCancelEdit, handleEditConfig, handleJobAction, handleSaveConfig, handleSiteConfigChange, setShowSiteConfig, getAllLogs } = useMigrationDashboard(initialSummary);
12
+ return /*#__PURE__*/ jsxs("div", {
13
+ className: "gutter--left gutter--right collection-list__wrap",
14
+ children: [
15
+ /*#__PURE__*/ jsx("div", {
16
+ className: styles.migrationDashboardHeader,
17
+ children: /*#__PURE__*/ jsx("h1", {
18
+ children: "WordPress Migration Dashboard"
19
+ })
20
+ }),
21
+ /*#__PURE__*/ jsx(SiteConfigPanel, {
22
+ configState: configState,
23
+ loading: loading,
24
+ onCancel: handleCancelEdit,
25
+ onConfigChange: handleSiteConfigChange,
26
+ onEdit: handleEditConfig,
27
+ onSave: handleSaveConfig,
28
+ onToggle: ()=>setShowSiteConfig(!showSiteConfig),
29
+ showSiteConfig: showSiteConfig,
30
+ siteConfig: siteConfig
31
+ }),
32
+ /*#__PURE__*/ jsx(StatsOverview, {
33
+ siteConfig: siteConfig,
34
+ summary: summary
35
+ }),
36
+ /*#__PURE__*/ jsx(JobsTable, {
37
+ jobs: summary.recentJobs ?? [],
38
+ loading: loading,
39
+ onJobAction: handleJobAction
40
+ }),
41
+ /*#__PURE__*/ jsx(LogViewer, {
42
+ logs: getAllLogs()
43
+ })
44
+ ]
45
+ });
46
+ };
47
+
48
+ export { MigrationDashboardClient };
49
+ //# sourceMappingURL=MigrationDashboardClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MigrationDashboardClient.js","sources":["../../src/components/MigrationDashboardClient.tsx"],"sourcesContent":["'use client'\n\nimport { JobsTable } from './dashboard/JobsTable'\nimport { LogViewer } from './dashboard/LogViewer'\nimport { SiteConfigPanel } from './dashboard/SiteConfigPanel'\nimport { StatsOverview } from './dashboard/StatsOverview'\nimport type { MigrationSummary } from './dashboard/types'\nimport { useMigrationDashboard } from './dashboard/useMigrationDashboard'\n\nimport styles from './MigrationDashboardClient.module.css'\n\ntype MigrationDashboardClientProps = {\n summary: MigrationSummary\n}\n\nexport const MigrationDashboardClient = ({\n summary: initialSummary,\n}: MigrationDashboardClientProps) => {\n const {\n configState,\n loading,\n showSiteConfig,\n siteConfig,\n summary,\n handleCancelEdit,\n handleEditConfig,\n handleJobAction,\n handleSaveConfig,\n handleSiteConfigChange,\n setShowSiteConfig,\n getAllLogs,\n } = useMigrationDashboard(initialSummary)\n\n return (\n <div className=\"gutter--left gutter--right collection-list__wrap\">\n <div className={styles.migrationDashboardHeader}>\n <h1>WordPress Migration Dashboard</h1>\n </div>\n\n <SiteConfigPanel\n configState={configState}\n loading={loading}\n onCancel={handleCancelEdit}\n onConfigChange={handleSiteConfigChange}\n onEdit={handleEditConfig}\n onSave={handleSaveConfig}\n onToggle={() => setShowSiteConfig(!showSiteConfig)}\n showSiteConfig={showSiteConfig}\n siteConfig={siteConfig}\n />\n\n <StatsOverview siteConfig={siteConfig} summary={summary} />\n\n <JobsTable\n jobs={summary.recentJobs ?? []}\n loading={loading}\n onJobAction={handleJobAction}\n />\n\n <LogViewer logs={getAllLogs()} />\n </div>\n )\n}\n"],"names":["className"],"mappings":";;;;;;;;;;;AAiCE;;;;AAESA;AACH;AAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0BZ;;"}