payload-wordpress-migrator 0.0.22 → 0.0.24

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.
@@ -63,22 +63,14 @@ const FieldMappingConfiguration = ({ path })=>{
63
63
  });
64
64
  return flattened;
65
65
  };
66
- // Fetch WordPress content type fields
66
+ // Fetch WordPress content type fields (credentials resolved server-side from plugin config)
67
67
  const fetchWordPressFields = useCallback(async (contentType)=>{
68
68
  try {
69
69
  setLoading(true);
70
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
71
  const response = await fetch('/api/wordpress/content-fields', {
77
72
  body: JSON.stringify({
78
- contentType,
79
- wpPassword: config.wpPassword,
80
- wpSiteUrl: config.wpSiteUrl,
81
- wpUsername: config.wpUsername
73
+ contentType
82
74
  }),
83
75
  headers: {
84
76
  'Content-Type': 'application/json'
@@ -1 +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;;"}
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 (credentials resolved server-side from plugin config)\n const fetchWordPressFields = useCallback(async (contentType: string) => {\n try {\n setLoading(true)\n setError('')\n\n const response = await fetch('/api/wordpress/content-fields', {\n body: JSON.stringify({ contentType }),\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","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;;;;;;;;AAS6BG;AAAY;;;AAGnC;;AAEF;;AAIA;AACE;;;AAGA;AACF;AACF;;AAEEC;AACAC;;;AAGF;;;;;;;;;AAUI;;;AAGA;AACF;AACF;;AAEEC;AACF;;;;;AAKA;AACE;AACA;;AAEAD;AACAC;AACAR;AACF;;AACES;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;;;AACMd;;;AACHc;AAAmCd;;;AAFJA;;;;;AAQxCc;;;;AACC;AAAiBC;AAAoC;;;;AACpDD;AACFG;AACMH;;;AACMd;;;AACHc;AAAmCd;;;AAFJA;;;;;;;;AAU5Cc;;;AACEA;;;AACC;;;;;;AAKH;;;;AAKFL;AACMK;;;;;AAIDjB;;;;AAEiB;;AAChBmB;AAC0BnB;;AACtBG;AAAW;AAAGA;AAAW;;AADfA;;;;AAMXc;AAAqC;;;;;AAKzCjB;;;;AAEiB;;AAChBoB;AAC0BpB;;AACtBG;AAAW;AAAGA;AAAW;;AADfA;;;;;AAQfkB;;AAED;;;AAjC0CC;;AAwCxCL;AAAoC;;;;;;;;AASvD;;"}
@@ -8,7 +8,7 @@ import { useMigrationDashboard } from './dashboard/useMigrationDashboard.js';
8
8
  import styles from './MigrationDashboardClient.module.css';
9
9
 
10
10
  const MigrationDashboardClient = ({ summary: initialSummary })=>{
11
- const { configState, loading, showSiteConfig, siteConfig, summary, handleCancelEdit, handleEditConfig, handleJobAction, handleSaveConfig, handleSiteConfigChange, setShowSiteConfig, getAllLogs } = useMigrationDashboard(initialSummary);
11
+ const { loading, siteConfig, summary, handleJobAction, scanWordPressContent, getAllLogs } = useMigrationDashboard(initialSummary);
12
12
  return /*#__PURE__*/ jsxs("div", {
13
13
  className: "gutter--left gutter--right collection-list__wrap",
14
14
  children: [
@@ -19,14 +19,8 @@ const MigrationDashboardClient = ({ summary: initialSummary })=>{
19
19
  })
20
20
  }),
21
21
  /*#__PURE__*/ jsx(SiteConfigPanel, {
22
- configState: configState,
23
22
  loading: loading,
24
- onCancel: handleCancelEdit,
25
- onConfigChange: handleSiteConfigChange,
26
- onEdit: handleEditConfig,
27
- onSave: handleSaveConfig,
28
- onToggle: ()=>setShowSiteConfig(!showSiteConfig),
29
- showSiteConfig: showSiteConfig,
23
+ onScan: scanWordPressContent,
30
24
  siteConfig: siteConfig
31
25
  }),
32
26
  /*#__PURE__*/ jsx(StatsOverview, {
@@ -1 +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;;"}
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 loading,\n siteConfig,\n summary,\n handleJobAction,\n scanWordPressContent,\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 loading={loading}\n onScan={scanWordPressContent}\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":";;;;;;;;;;AAkBE;AASA;;;;AAESA;AACH;AAAI;;;;;;;;;;;;;;;;;;;;;;AAoBZ;;"}
@@ -27,24 +27,15 @@ const SimpleFieldMapping = ({ path })=>{
27
27
  ]);
28
28
  const [loading, setLoading] = useState(false);
29
29
  const [error, setError] = useState('');
30
- // Fetch WordPress content fields
30
+ // Fetch WordPress content fields (credentials resolved server-side from plugin config)
31
31
  const fetchWordPressFields = async (contentType)=>{
32
32
  try {
33
33
  setLoading(true);
34
34
  setError('');
35
- const savedConfig = localStorage.getItem('wp-site-config');
36
- if (!savedConfig) {
37
- throw new Error('WordPress site configuration not found');
38
- }
39
- const config = JSON.parse(savedConfig);
40
- const requestBody = {
41
- contentType,
42
- wpPassword: config.wpPassword,
43
- wpSiteUrl: config.wpSiteUrl,
44
- wpUsername: config.wpUsername
45
- };
46
35
  const response = await fetch('/api/wordpress/content-fields', {
47
- body: JSON.stringify(requestBody),
36
+ body: JSON.stringify({
37
+ contentType
38
+ }),
48
39
  headers: {
49
40
  'Content-Type': 'application/json'
50
41
  },
@@ -1 +1 @@
1
- {"version":3,"file":"SimpleFieldMapping.js","sources":["../../src/components/SimpleFieldMapping.tsx"],"sourcesContent":["'use client'\n\nimport { useField } from '@payloadcms/ui'\nimport React, { useEffect, useRef, useState } from 'react'\n\ntype FieldMapping = {\n destinationField: string\n sourceField: string\n}\n\ntype Option = {\n label: string\n path?: string\n type?: string\n value: string\n}\n\nconst SimpleFieldMapping: 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 // Use ref to prevent infinite loops - change to any type to handle different value types\n const isUpdatingRef = useRef(false)\n const lastValueRef = useRef<any>('')\n\n // Dynamic field options from API\n const [sourceFields, setSourceFields] = useState<Option[]>([])\n const [destinationFields, setDestinationFields] = useState<Option[]>([])\n const [fieldMappings, setFieldMappings] = useState<FieldMapping[]>([\n { destinationField: '', sourceField: '' },\n ])\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState('')\n\n // Fetch WordPress content fields\n const fetchWordPressFields = 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 requestBody = {\n contentType,\n wpPassword: config.wpPassword,\n wpSiteUrl: config.wpSiteUrl,\n wpUsername: config.wpUsername,\n }\n\n const response = await fetch('/api/wordpress/content-fields', {\n body: JSON.stringify(requestBody),\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n })\n const result = await response.json()\n\n if (response.ok && result.success) {\n const wpFieldOptions = (result.fields || []).map((field: any) => ({\n type: field.type,\n label: `${field.label} (${field.type})${field.custom ? ' *' : ''}`,\n path: field.path || field.name,\n value: field.path || field.name,\n }))\n setSourceFields(wpFieldOptions)\n } else {\n console.error('API returned error:', result)\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 setSourceFields([])\n }\n }\n\n // Fetch PayloadCMS collection fields\n const fetchPayloadFields = async (collectionSlug: string) => {\n try {\n const response = await fetch(`/api/collections/${collectionSlug}/fields`)\n\n const result = await response.json()\n\n if (response.ok && result.success) {\n const payloadFieldOptions = (result.fields || []).map((field: any) => ({\n type: field.type,\n label: `${field.label} (${field.type})`,\n path: field.path || field.name,\n value: field.path || field.name,\n }))\n setDestinationFields(payloadFieldOptions)\n } else {\n console.error('Payload API returned error:', result)\n throw new Error(result.error || 'Failed to fetch Payload collection fields')\n }\n } catch (error) {\n console.error('Error fetching Payload fields:', error)\n setDestinationFields([])\n }\n }\n\n // Fetch fields when both content type and target collection are selected\n useEffect(() => {\n const fetchFields = async () => {\n if (contentTypeValue && targetCollectionValue) {\n setLoading(true)\n try {\n await Promise.all([\n fetchWordPressFields(contentTypeValue as string),\n fetchPayloadFields(targetCollectionValue as string),\n ])\n } catch (error) {\n console.error('Error fetching fields:', error)\n } finally {\n setLoading(false)\n }\n } else {\n setSourceFields([])\n setDestinationFields([])\n setFieldMappings([{ destinationField: '', sourceField: '' }])\n setError('')\n }\n }\n\n void fetchFields()\n }, [contentTypeValue, targetCollectionValue])\n\n // Parse existing field mappings from JSON value - only when value actually changes\n useEffect(() => {\n if (isUpdatingRef.current) {\n return\n } // Prevent processing our own updates\n\n // Convert value to string for comparison\n const currentValue = typeof value === 'string' ? value : JSON.stringify(value || '')\n\n if (value && currentValue !== lastValueRef.current) {\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 lastValueRef.current = currentValue\n } catch (error) {\n console.warn('Failed to parse field mappings:', error)\n }\n }\n }, [value])\n\n // Function to update parent value without causing loops\n const updateParentValue = (mappings: FieldMapping[]) => {\n const newValue = JSON.stringify({\n contentType: contentTypeValue,\n fieldMappings: mappings,\n targetCollection: targetCollectionValue,\n })\n\n // Prevent infinite loops\n if (newValue !== lastValueRef.current && !isUpdatingRef.current) {\n isUpdatingRef.current = true\n setValue(newValue)\n lastValueRef.current = newValue\n\n // Reset the flag after a short delay\n setTimeout(() => {\n isUpdatingRef.current = false\n }, 100)\n }\n }\n\n // Add new mapping row\n const handleAddMapping = () => {\n const newMappings = [...fieldMappings, { destinationField: '', sourceField: '' }]\n setFieldMappings(newMappings)\n updateParentValue(newMappings)\n }\n\n // Remove mapping row\n const handleRemoveMapping = (idx: number) => {\n if (fieldMappings.length <= 1) {\n return\n } // Keep at least one mapping\n\n const newMappings = fieldMappings.filter((_, i) => i !== idx)\n setFieldMappings(newMappings)\n updateParentValue(newMappings)\n }\n\n // Update mapping\n const handleChange = (idx: number, field: keyof FieldMapping, newValue: string) => {\n const newMappings = fieldMappings.map((mapping, i) =>\n i === idx ? { ...mapping, [field]: newValue } : mapping,\n )\n setFieldMappings(newMappings)\n updateParentValue(newMappings)\n }\n\n // Filter dropdown options to exclude already-mapped fields (except for current row)\n const getFilteredOptions = (options: Option[], field: keyof FieldMapping, idx: number) => {\n const used = fieldMappings\n .map((m, i) => (i !== idx ? m[field] : null))\n .filter(Boolean) as string[]\n return options.filter((opt) => !used.includes(opt.value))\n }\n\n if (!contentTypeValue || !targetCollectionValue) {\n return (\n <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>\n <label style={{ color: 'var(--theme-elevation-700)', fontSize: '14px', fontWeight: '600' }}>\n Field Mapping Configuration\n </label>\n <div\n style={{\n backgroundColor: 'var(--theme-elevation-50)',\n border: '1px solid var(--theme-elevation-200)',\n borderRadius: '4px',\n color: 'var(--theme-elevation-600)',\n padding: '1rem',\n textAlign: 'center',\n }}\n >\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 style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>\n <div style={{ alignItems: 'center', display: 'flex', justifyContent: 'space-between' }}>\n <label style={{ color: 'var(--theme-elevation-700)', fontSize: '14px', fontWeight: '600' }}>\n Field Mapping Configuration\n </label>\n <button\n disabled={loading}\n onClick={handleAddMapping}\n style={{\n backgroundColor: loading ? 'var(--theme-elevation-200)' : 'var(--theme-success-600)',\n border: 'none',\n borderRadius: '4px',\n color: loading ? 'var(--theme-elevation-500)' : 'white',\n cursor: loading ? 'not-allowed' : 'pointer',\n fontSize: '12px',\n fontWeight: '500',\n padding: '0.5rem 1rem',\n }}\n type=\"button\"\n >\n Add Mapping\n </button>\n </div>\n\n {loading && (\n <div\n style={{\n backgroundColor: 'var(--theme-elevation-50)',\n border: '1px solid var(--theme-elevation-200)',\n borderRadius: '4px',\n color: 'var(--theme-elevation-600)',\n padding: '1rem',\n textAlign: 'center',\n }}\n >\n Loading field schemas from WordPress and PayloadCMS...\n </div>\n )}\n\n {error && (\n <div\n style={{\n backgroundColor: 'var(--theme-error-50)',\n border: '1px solid var(--theme-error-200)',\n borderRadius: '4px',\n color: 'var(--theme-error-700)',\n padding: '1rem',\n }}\n >\n <strong>Error:</strong> {error}\n </div>\n )}\n\n {!loading && !error && sourceFields.length === 0 && destinationFields.length === 0 && (\n <div\n style={{\n backgroundColor: 'var(--theme-elevation-50)',\n border: '1px solid var(--theme-elevation-200)',\n borderRadius: '4px',\n color: 'var(--theme-elevation-600)',\n padding: '1rem',\n textAlign: 'center',\n }}\n >\n No fields found. Please check your WordPress connection and selected content type.\n </div>\n )}\n\n {!loading && !error && (sourceFields.length > 0 || destinationFields.length > 0) && (\n <>\n {fieldMappings.length === 0 && (\n <div\n style={{\n backgroundColor: 'var(--theme-elevation-50)',\n border: '1px solid var(--theme-elevation-200)',\n borderRadius: '4px',\n color: 'var(--theme-elevation-600)',\n padding: '1rem',\n textAlign: 'center',\n }}\n >\n No field mappings configured. Click \"Add Mapping\" to start.\n </div>\n )}\n\n {fieldMappings.map((mapping, idx) => (\n <div\n key={idx}\n style={{\n alignItems: 'center',\n backgroundColor: 'var(--theme-elevation-0)',\n border: '1px solid var(--theme-elevation-200)',\n borderRadius: '4px',\n display: 'grid',\n gap: '0.75rem',\n gridTemplateColumns: '1fr auto 1fr auto',\n padding: '1rem',\n }}\n >\n <select\n onChange={(e) => handleChange(idx, 'sourceField', e.target.value)}\n style={{\n backgroundColor: 'var(--theme-elevation-0)',\n border: '1px solid var(--theme-elevation-200)',\n borderRadius: '4px',\n fontSize: '14px',\n padding: '0.5rem',\n }}\n value={mapping.sourceField}\n >\n <option value=\"\">Select WordPress field...</option>\n {getFilteredOptions(sourceFields, 'sourceField', idx).map((opt, optIdx) => (\n <option key={`source-${idx}-${opt.value}-${optIdx}`} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n\n <span\n style={{\n color: 'var(--theme-elevation-400)',\n fontSize: '18px',\n fontWeight: 'bold',\n }}\n >\n →\n </span>\n\n <select\n onChange={(e) => handleChange(idx, 'destinationField', e.target.value)}\n style={{\n backgroundColor: 'var(--theme-elevation-0)',\n border: '1px solid var(--theme-elevation-200)',\n borderRadius: '4px',\n fontSize: '14px',\n padding: '0.5rem',\n }}\n value={mapping.destinationField}\n >\n <option value=\"\">Select Payload field...</option>\n {getFilteredOptions(destinationFields, 'destinationField', idx).map(\n (opt, optIdx) => (\n <option key={`dest-${idx}-${opt.value}-${optIdx}`} value={opt.value}>\n {opt.label}\n </option>\n ),\n )}\n </select>\n\n <button\n disabled={fieldMappings.length <= 1}\n onClick={() => handleRemoveMapping(idx)}\n style={{\n backgroundColor:\n fieldMappings.length <= 1\n ? 'var(--theme-elevation-200)'\n : 'var(--theme-error-600)',\n border: 'none',\n borderRadius: '4px',\n color: fieldMappings.length <= 1 ? 'var(--theme-elevation-500)' : 'white',\n cursor: fieldMappings.length <= 1 ? 'not-allowed' : 'pointer',\n fontSize: '12px',\n fontWeight: '500',\n minWidth: '60px',\n padding: '0.5rem',\n }}\n type=\"button\"\n >\n Remove\n </button>\n </div>\n ))}\n </>\n )}\n\n <div\n style={{\n color: 'var(--theme-elevation-500)',\n fontSize: '12px',\n fontStyle: 'italic',\n marginTop: '0.5rem',\n }}\n >\n Map WordPress fields to their corresponding Payload fields. Each field can only be mapped\n once. Fields marked with * are custom fields (ACF/Meta).\n {sourceFields.length > 0 && (\n <>\n <br />\n <strong>Available WordPress fields:</strong> {sourceFields.length} |{' '}\n <strong>Available Payload fields:</strong> {destinationFields.length}\n </>\n )}\n </div>\n </div>\n )\n}\n\nexport default SimpleFieldMapping\n"],"names":["path","contentType","wpPassword","wpSiteUrl","wpUsername","type","label","value","setError","setSourceFields","setDestinationFields","contentTypeValue","targetCollectionValue","setFieldMappings","lastValueRef","isUpdatingRef","fieldMappings","backgroundColor","color","cursor","loading","error","opt","onClick","idx","sourceFields","destinationFields"],"mappings":";;;;;AAiBA;AACE;AAAuCA;AAAK;AAC5C;;AAAmE;AACnE;;AAA6E;;AAG7E;AACA;;AAGA;AACA;AACA;AACE;;;AAAwC;AACzC;AACD;AACA;;AAGA;;;;;AAMI;AACE;AACF;;AAIA;AACEC;AACAC;AACAC;AACAC;AACF;;;;;AAME;;AAEF;;AAGA;AACE;AACEC;AACAC;AACAN;AACAO;;;;;AAKF;AACF;AACF;;AAEEC;AACAC;AACF;AACF;;AAGA;;;;AAMI;AACE;AACEJ;;AAEAL;AACAO;;;;;AAKF;AACF;AACF;;AAEEG;AACF;AACF;;;AAIE;AACE;;;;;;AAMK;AACH;;;;AAIA;;AAEAD;AACAC;;AACkB;;;AAAwC;AAAE;;AAE9D;AACF;;;AAGEC;AAAkBC;AAAsB;;;;AAKxC;AACF;;;AAKA;;AAEI;AACA;AACEC;AACF;AACAC;AACF;;AAEA;AACF;;AACEP;AAAM;;AAGV;;;;;AAKE;;AAGA;AACEQ;;AAEAD;;;AAIEC;;AAEJ;AACF;;AAGA;AACE;AAAwBC;AAAe;;;AAAwC;AAAE;;;AAGnF;;AAGA;;AAEI;AACF;AAEA;;;AAGF;;;;AAKkB;AAAY;;;;AAI9B;;;AAIE;;AAIF;;AAGE;;;;;AACsE;;;;;;;AACuB;AAAG;;;;;;;;;;AAW1F;AACD;;;;AAMP;AAEA;;;;;AACsE;;;;;;;AACmB;;;;;;;AACM;AAAG;;;;;;AAOxFC;;;AAGAC;AACAC;;;;AAIF;;AAED;;;;AAKFC;;;;;;;;AASG;AACD;;AAKFC;;;;;;;AAQG;;;AAEQ;;AAAe;AAAEA;;;;;;;;;;;AAazB;AACD;;AAKF;;;;;;;;;;AAWO;AACD;;AAKFL;;;;;;;;;;AAYG;;;;;;;;;;AAUE;AACAT;;;;AAEiB;;;AAEsCA;AAClDe;;;;;;;;;AAUL;AACD;;;;;;;;;;AAYC;AACAf;;;;AAEiB;;;AAGsCA;AAChDe;;;;;;AAQPC;;AAEEN;;;AAMAC;AACAC;;;;;AAKF;;AAED;;;AAhFIK;;;;;;;;;AA8FX;;AACD;;;;;AAMa;;AAAoC;AAAEC;AAAoB;AAAG;;AAC7D;;AAAkC;AAAEC;;;;;;;AAMxD;;"}
1
+ {"version":3,"file":"SimpleFieldMapping.js","sources":["../../src/components/SimpleFieldMapping.tsx"],"sourcesContent":["'use client'\n\nimport { useField } from '@payloadcms/ui'\nimport React, { useEffect, useRef, useState } from 'react'\n\ntype FieldMapping = {\n destinationField: string\n sourceField: string\n}\n\ntype Option = {\n label: string\n path?: string\n type?: string\n value: string\n}\n\nconst SimpleFieldMapping: 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 // Use ref to prevent infinite loops - change to any type to handle different value types\n const isUpdatingRef = useRef(false)\n const lastValueRef = useRef<any>('')\n\n // Dynamic field options from API\n const [sourceFields, setSourceFields] = useState<Option[]>([])\n const [destinationFields, setDestinationFields] = useState<Option[]>([])\n const [fieldMappings, setFieldMappings] = useState<FieldMapping[]>([\n { destinationField: '', sourceField: '' },\n ])\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState('')\n\n // Fetch WordPress content fields (credentials resolved server-side from plugin config)\n const fetchWordPressFields = async (contentType: string) => {\n try {\n setLoading(true)\n setError('')\n\n const response = await fetch('/api/wordpress/content-fields', {\n body: JSON.stringify({ contentType }),\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n })\n const result = await response.json()\n\n if (response.ok && result.success) {\n const wpFieldOptions = (result.fields || []).map((field: any) => ({\n type: field.type,\n label: `${field.label} (${field.type})${field.custom ? ' *' : ''}`,\n path: field.path || field.name,\n value: field.path || field.name,\n }))\n setSourceFields(wpFieldOptions)\n } else {\n console.error('API returned error:', result)\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 setSourceFields([])\n }\n }\n\n // Fetch PayloadCMS collection fields\n const fetchPayloadFields = async (collectionSlug: string) => {\n try {\n const response = await fetch(`/api/collections/${collectionSlug}/fields`)\n\n const result = await response.json()\n\n if (response.ok && result.success) {\n const payloadFieldOptions = (result.fields || []).map((field: any) => ({\n type: field.type,\n label: `${field.label} (${field.type})`,\n path: field.path || field.name,\n value: field.path || field.name,\n }))\n setDestinationFields(payloadFieldOptions)\n } else {\n console.error('Payload API returned error:', result)\n throw new Error(result.error || 'Failed to fetch Payload collection fields')\n }\n } catch (error) {\n console.error('Error fetching Payload fields:', error)\n setDestinationFields([])\n }\n }\n\n // Fetch fields when both content type and target collection are selected\n useEffect(() => {\n const fetchFields = async () => {\n if (contentTypeValue && targetCollectionValue) {\n setLoading(true)\n try {\n await Promise.all([\n fetchWordPressFields(contentTypeValue as string),\n fetchPayloadFields(targetCollectionValue as string),\n ])\n } catch (error) {\n console.error('Error fetching fields:', error)\n } finally {\n setLoading(false)\n }\n } else {\n setSourceFields([])\n setDestinationFields([])\n setFieldMappings([{ destinationField: '', sourceField: '' }])\n setError('')\n }\n }\n\n void fetchFields()\n }, [contentTypeValue, targetCollectionValue])\n\n // Parse existing field mappings from JSON value - only when value actually changes\n useEffect(() => {\n if (isUpdatingRef.current) {\n return\n } // Prevent processing our own updates\n\n // Convert value to string for comparison\n const currentValue = typeof value === 'string' ? value : JSON.stringify(value || '')\n\n if (value && currentValue !== lastValueRef.current) {\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 lastValueRef.current = currentValue\n } catch (error) {\n console.warn('Failed to parse field mappings:', error)\n }\n }\n }, [value])\n\n // Function to update parent value without causing loops\n const updateParentValue = (mappings: FieldMapping[]) => {\n const newValue = JSON.stringify({\n contentType: contentTypeValue,\n fieldMappings: mappings,\n targetCollection: targetCollectionValue,\n })\n\n // Prevent infinite loops\n if (newValue !== lastValueRef.current && !isUpdatingRef.current) {\n isUpdatingRef.current = true\n setValue(newValue)\n lastValueRef.current = newValue\n\n // Reset the flag after a short delay\n setTimeout(() => {\n isUpdatingRef.current = false\n }, 100)\n }\n }\n\n // Add new mapping row\n const handleAddMapping = () => {\n const newMappings = [...fieldMappings, { destinationField: '', sourceField: '' }]\n setFieldMappings(newMappings)\n updateParentValue(newMappings)\n }\n\n // Remove mapping row\n const handleRemoveMapping = (idx: number) => {\n if (fieldMappings.length <= 1) {\n return\n } // Keep at least one mapping\n\n const newMappings = fieldMappings.filter((_, i) => i !== idx)\n setFieldMappings(newMappings)\n updateParentValue(newMappings)\n }\n\n // Update mapping\n const handleChange = (idx: number, field: keyof FieldMapping, newValue: string) => {\n const newMappings = fieldMappings.map((mapping, i) =>\n i === idx ? { ...mapping, [field]: newValue } : mapping,\n )\n setFieldMappings(newMappings)\n updateParentValue(newMappings)\n }\n\n // Filter dropdown options to exclude already-mapped fields (except for current row)\n const getFilteredOptions = (options: Option[], field: keyof FieldMapping, idx: number) => {\n const used = fieldMappings\n .map((m, i) => (i !== idx ? m[field] : null))\n .filter(Boolean) as string[]\n return options.filter((opt) => !used.includes(opt.value))\n }\n\n if (!contentTypeValue || !targetCollectionValue) {\n return (\n <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>\n <label style={{ color: 'var(--theme-elevation-700)', fontSize: '14px', fontWeight: '600' }}>\n Field Mapping Configuration\n </label>\n <div\n style={{\n backgroundColor: 'var(--theme-elevation-50)',\n border: '1px solid var(--theme-elevation-200)',\n borderRadius: '4px',\n color: 'var(--theme-elevation-600)',\n padding: '1rem',\n textAlign: 'center',\n }}\n >\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 style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>\n <div style={{ alignItems: 'center', display: 'flex', justifyContent: 'space-between' }}>\n <label style={{ color: 'var(--theme-elevation-700)', fontSize: '14px', fontWeight: '600' }}>\n Field Mapping Configuration\n </label>\n <button\n disabled={loading}\n onClick={handleAddMapping}\n style={{\n backgroundColor: loading ? 'var(--theme-elevation-200)' : 'var(--theme-success-600)',\n border: 'none',\n borderRadius: '4px',\n color: loading ? 'var(--theme-elevation-500)' : 'white',\n cursor: loading ? 'not-allowed' : 'pointer',\n fontSize: '12px',\n fontWeight: '500',\n padding: '0.5rem 1rem',\n }}\n type=\"button\"\n >\n Add Mapping\n </button>\n </div>\n\n {loading && (\n <div\n style={{\n backgroundColor: 'var(--theme-elevation-50)',\n border: '1px solid var(--theme-elevation-200)',\n borderRadius: '4px',\n color: 'var(--theme-elevation-600)',\n padding: '1rem',\n textAlign: 'center',\n }}\n >\n Loading field schemas from WordPress and PayloadCMS...\n </div>\n )}\n\n {error && (\n <div\n style={{\n backgroundColor: 'var(--theme-error-50)',\n border: '1px solid var(--theme-error-200)',\n borderRadius: '4px',\n color: 'var(--theme-error-700)',\n padding: '1rem',\n }}\n >\n <strong>Error:</strong> {error}\n </div>\n )}\n\n {!loading && !error && sourceFields.length === 0 && destinationFields.length === 0 && (\n <div\n style={{\n backgroundColor: 'var(--theme-elevation-50)',\n border: '1px solid var(--theme-elevation-200)',\n borderRadius: '4px',\n color: 'var(--theme-elevation-600)',\n padding: '1rem',\n textAlign: 'center',\n }}\n >\n No fields found. Please check your WordPress connection and selected content type.\n </div>\n )}\n\n {!loading && !error && (sourceFields.length > 0 || destinationFields.length > 0) && (\n <>\n {fieldMappings.length === 0 && (\n <div\n style={{\n backgroundColor: 'var(--theme-elevation-50)',\n border: '1px solid var(--theme-elevation-200)',\n borderRadius: '4px',\n color: 'var(--theme-elevation-600)',\n padding: '1rem',\n textAlign: 'center',\n }}\n >\n No field mappings configured. Click \"Add Mapping\" to start.\n </div>\n )}\n\n {fieldMappings.map((mapping, idx) => (\n <div\n key={idx}\n style={{\n alignItems: 'center',\n backgroundColor: 'var(--theme-elevation-0)',\n border: '1px solid var(--theme-elevation-200)',\n borderRadius: '4px',\n display: 'grid',\n gap: '0.75rem',\n gridTemplateColumns: '1fr auto 1fr auto',\n padding: '1rem',\n }}\n >\n <select\n onChange={(e) => handleChange(idx, 'sourceField', e.target.value)}\n style={{\n backgroundColor: 'var(--theme-elevation-0)',\n border: '1px solid var(--theme-elevation-200)',\n borderRadius: '4px',\n fontSize: '14px',\n padding: '0.5rem',\n }}\n value={mapping.sourceField}\n >\n <option value=\"\">Select WordPress field...</option>\n {getFilteredOptions(sourceFields, 'sourceField', idx).map((opt, optIdx) => (\n <option key={`source-${idx}-${opt.value}-${optIdx}`} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n\n <span\n style={{\n color: 'var(--theme-elevation-400)',\n fontSize: '18px',\n fontWeight: 'bold',\n }}\n >\n →\n </span>\n\n <select\n onChange={(e) => handleChange(idx, 'destinationField', e.target.value)}\n style={{\n backgroundColor: 'var(--theme-elevation-0)',\n border: '1px solid var(--theme-elevation-200)',\n borderRadius: '4px',\n fontSize: '14px',\n padding: '0.5rem',\n }}\n value={mapping.destinationField}\n >\n <option value=\"\">Select Payload field...</option>\n {getFilteredOptions(destinationFields, 'destinationField', idx).map(\n (opt, optIdx) => (\n <option key={`dest-${idx}-${opt.value}-${optIdx}`} value={opt.value}>\n {opt.label}\n </option>\n ),\n )}\n </select>\n\n <button\n disabled={fieldMappings.length <= 1}\n onClick={() => handleRemoveMapping(idx)}\n style={{\n backgroundColor:\n fieldMappings.length <= 1\n ? 'var(--theme-elevation-200)'\n : 'var(--theme-error-600)',\n border: 'none',\n borderRadius: '4px',\n color: fieldMappings.length <= 1 ? 'var(--theme-elevation-500)' : 'white',\n cursor: fieldMappings.length <= 1 ? 'not-allowed' : 'pointer',\n fontSize: '12px',\n fontWeight: '500',\n minWidth: '60px',\n padding: '0.5rem',\n }}\n type=\"button\"\n >\n Remove\n </button>\n </div>\n ))}\n </>\n )}\n\n <div\n style={{\n color: 'var(--theme-elevation-500)',\n fontSize: '12px',\n fontStyle: 'italic',\n marginTop: '0.5rem',\n }}\n >\n Map WordPress fields to their corresponding Payload fields. Each field can only be mapped\n once. Fields marked with * are custom fields (ACF/Meta).\n {sourceFields.length > 0 && (\n <>\n <br />\n <strong>Available WordPress fields:</strong> {sourceFields.length} |{' '}\n <strong>Available Payload fields:</strong> {destinationFields.length}\n </>\n )}\n </div>\n </div>\n )\n}\n\nexport default SimpleFieldMapping\n"],"names":["path","contentType","type","label","value","setError","setSourceFields","setDestinationFields","contentTypeValue","targetCollectionValue","setFieldMappings","lastValueRef","isUpdatingRef","fieldMappings","backgroundColor","color","cursor","loading","error","opt","onClick","idx","sourceFields","destinationFields"],"mappings":";;;;;AAiBA;AACE;AAAuCA;AAAK;AAC5C;;AAAmE;AACnE;;AAA6E;;AAG7E;AACA;;AAGA;AACA;AACA;AACE;;;AAAwC;AACzC;AACD;AACA;;AAGA;;;;;;AAM6BC;AAAY;;;AAGnC;;AAEF;;AAGA;AACE;AACEC;AACAC;AACAH;AACAI;;;;;AAKF;AACF;AACF;;AAEEC;AACAC;AACF;AACF;;AAGA;;;;AAMI;AACE;AACEJ;;AAEAF;AACAI;;;;;AAKF;AACF;AACF;;AAEEG;AACF;AACF;;;AAIE;AACE;;;;;;AAMK;AACH;;;;AAIA;;AAEAD;AACAC;;AACkB;;;AAAwC;AAAE;;AAE9D;AACF;;;AAGEC;AAAkBC;AAAsB;;;;AAKxC;AACF;;;AAKA;;AAEI;AACA;AACEC;AACF;AACAC;AACF;;AAEA;AACF;;AACEP;AAAM;;AAGV;;;;;AAKE;;AAGA;AACEQ;;AAEAD;;;AAIEC;;AAEJ;AACF;;AAGA;AACE;AAAwBC;AAAe;;;AAAwC;AAAE;;;AAGnF;;AAGA;;AAEI;AACF;AAEA;;;AAGF;;;;AAKkB;AAAY;;;;AAI9B;;;AAIE;;AAIF;;AAGE;;;;;AACsE;;;;;;;AACuB;AAAG;;;;;;;;;;AAW1F;AACD;;;;AAMP;AAEA;;;;;AACsE;;;;;;;AACmB;;;;;;;AACM;AAAG;;;;;;AAOxFC;;;AAGAC;AACAC;;;;AAIF;;AAED;;;;AAKFC;;;;;;;;AASG;AACD;;AAKFC;;;;;;;AAQG;;;AAEQ;;AAAe;AAAEA;;;;;;;;;;;AAazB;AACD;;AAKF;;;;;;;;;;AAWO;AACD;;AAKFL;;;;;;;;;;AAYG;;;;;;;;;;AAUE;AACAT;;;;AAEiB;;;AAEsCA;AAClDe;;;;;;;;;AAUL;AACD;;;;;;;;;;AAYC;AACAf;;;;AAEiB;;;AAGsCA;AAChDe;;;;;;AAQPC;;AAEEN;;;AAMAC;AACAC;;;;;AAKF;;AAED;;;AAhFIK;;;;;;;;;AA8FX;;AACD;;;;;AAMa;;AAAoC;AAAEC;AAAoB;AAAG;;AAC7D;;AAAkC;AAAEC;;;;;;;AAMxD;;"}
@@ -1,12 +1,6 @@
1
- import type { SiteConfigState, WordPressSiteConfig } from './types';
2
- export declare const SiteConfigPanel: ({ configState, loading, onCancel, onConfigChange, onEdit, onSave, onToggle, showSiteConfig, siteConfig, }: {
3
- configState: SiteConfigState;
1
+ import type { WordPressSiteConfig } from './types';
2
+ export declare const SiteConfigPanel: ({ loading, onScan, siteConfig, }: {
4
3
  loading: boolean;
5
- onCancel: () => void;
6
- onConfigChange: (field: keyof WordPressSiteConfig, value: string) => void;
7
- onEdit: () => void;
8
- onSave: () => void;
9
- onToggle: () => void;
10
- showSiteConfig: boolean;
4
+ onScan: () => void;
11
5
  siteConfig: WordPressSiteConfig;
12
6
  }) => import("react/jsx-runtime").JSX.Element;
@@ -1,8 +1,8 @@
1
1
  "use client";
2
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
3
  import styles from '../MigrationDashboardClient.module.css';
4
4
 
5
- const SiteConfigPanel = ({ configState, loading, onCancel, onConfigChange, onEdit, onSave, onToggle, showSiteConfig, siteConfig })=>{
5
+ const SiteConfigPanel = ({ loading, onScan, siteConfig })=>{
6
6
  return /*#__PURE__*/ jsxs("div", {
7
7
  className: styles.siteConfigSection,
8
8
  children: [
@@ -10,192 +10,82 @@ const SiteConfigPanel = ({ configState, loading, onCancel, onConfigChange, onEdi
10
10
  className: styles.siteConfigHeader,
11
11
  children: [
12
12
  /*#__PURE__*/ jsx("h2", {
13
- children: "WordPress Site Configuration"
13
+ children: "WordPress Content Discovery"
14
14
  }),
15
15
  /*#__PURE__*/ jsx("button", {
16
- className: styles.toggleButton,
17
- onClick: onToggle,
18
- children: showSiteConfig ? 'Hide Configuration' : 'Show Configuration'
16
+ className: styles.saveButton,
17
+ disabled: loading || siteConfig.connectionStatus === 'scanning',
18
+ onClick: onScan,
19
+ children: siteConfig.connectionStatus === 'scanning' ? 'Scanning...' : 'Scan WordPress Content'
19
20
  })
20
21
  ]
21
22
  }),
22
- showSiteConfig && /*#__PURE__*/ jsxs("div", {
23
+ siteConfig.connectionStatus === 'scanned' && siteConfig.discoveredContent && siteConfig.discoveredContent.length > 0 && /*#__PURE__*/ jsx("div", {
23
24
  className: styles.siteConfigForm,
24
- children: [
25
- /*#__PURE__*/ jsxs("div", {
26
- className: styles.formGrid,
25
+ children: /*#__PURE__*/ jsx("div", {
26
+ className: styles.connectionStatus,
27
+ children: /*#__PURE__*/ jsxs("div", {
28
+ className: styles.discoveredContent,
27
29
  children: [
28
30
  /*#__PURE__*/ jsxs("div", {
29
- className: styles.formGroup,
30
- children: [
31
- /*#__PURE__*/ jsx("label", {
32
- htmlFor: "siteName",
33
- children: "Site Name"
34
- }),
35
- /*#__PURE__*/ jsx("input", {
36
- disabled: !configState.isEditing,
37
- id: "siteName",
38
- onChange: (e)=>onConfigChange('siteName', e.target.value),
39
- placeholder: "My WordPress Site",
40
- type: "text",
41
- value: siteConfig.siteName
42
- })
43
- ]
44
- }),
45
- /*#__PURE__*/ jsxs("div", {
46
- className: styles.formGroup,
47
- children: [
48
- /*#__PURE__*/ jsx("label", {
49
- htmlFor: "wpSiteUrl",
50
- children: "WordPress Site URL"
51
- }),
52
- /*#__PURE__*/ jsx("input", {
53
- disabled: !configState.isEditing,
54
- id: "wpSiteUrl",
55
- onChange: (e)=>onConfigChange('wpSiteUrl', e.target.value),
56
- placeholder: "https://example.com",
57
- type: "url",
58
- value: siteConfig.wpSiteUrl
59
- }),
60
- /*#__PURE__*/ jsx("small", {
61
- children: "Base URL of your WordPress site"
62
- })
63
- ]
64
- }),
65
- /*#__PURE__*/ jsxs("div", {
66
- className: styles.formGroup,
31
+ className: styles.discoveredHeader,
67
32
  children: [
68
- /*#__PURE__*/ jsx("label", {
69
- htmlFor: "wpUsername",
70
- children: "WordPress Username"
71
- }),
72
- /*#__PURE__*/ jsx("input", {
73
- disabled: !configState.isEditing,
74
- id: "wpUsername",
75
- onChange: (e)=>onConfigChange('wpUsername', e.target.value),
76
- placeholder: "admin",
77
- type: "text",
78
- value: siteConfig.wpUsername
79
- })
80
- ]
81
- }),
82
- /*#__PURE__*/ jsxs("div", {
83
- className: styles.formGroup,
84
- children: [
85
- /*#__PURE__*/ jsx("label", {
86
- htmlFor: "wpPassword",
87
- children: "Application Password"
88
- }),
89
- /*#__PURE__*/ jsx("input", {
90
- disabled: !configState.isEditing,
91
- id: "wpPassword",
92
- onChange: (e)=>onConfigChange('wpPassword', e.target.value),
93
- placeholder: "xxxx xxxx xxxx xxxx",
94
- type: "password",
95
- value: siteConfig.wpPassword
33
+ /*#__PURE__*/ jsxs("h4", {
34
+ children: [
35
+ "Available Content (",
36
+ siteConfig.totalItems,
37
+ " total items)"
38
+ ]
96
39
  }),
97
40
  /*#__PURE__*/ jsx("small", {
98
- children: "WordPress Application Password for REST API access"
41
+ children: "Ready for migration"
99
42
  })
100
43
  ]
101
- })
102
- ]
103
- }),
104
- /*#__PURE__*/ jsxs("div", {
105
- className: styles.configActions,
106
- children: [
107
- /*#__PURE__*/ jsx("div", {
108
- className: styles.connectionStatus,
109
- children: /*#__PURE__*/ jsxs("div", {
110
- className: styles.statusIndicator,
111
- children: [
112
- /*#__PURE__*/ jsx("span", {
113
- className: `${styles.statusDot} ${styles[siteConfig.connectionStatus]}`
114
- }),
115
- /*#__PURE__*/ jsxs("span", {
116
- className: styles.statusText,
117
- children: [
118
- siteConfig.connectionStatus === 'not-scanned' && 'Not Scanned',
119
- siteConfig.connectionStatus === 'scanned' && siteConfig.discoveredContent && siteConfig.discoveredContent.length > 0 && /*#__PURE__*/ jsxs("div", {
120
- className: styles.discoveredContent,
121
- children: [
122
- /*#__PURE__*/ jsxs("div", {
123
- className: styles.discoveredHeader,
124
- children: [
125
- /*#__PURE__*/ jsxs("h4", {
126
- children: [
127
- "Available Content (",
128
- siteConfig.totalItems,
129
- " total items)"
130
- ]
131
- }),
132
- /*#__PURE__*/ jsx("small", {
133
- children: "Ready for migration"
134
- })
135
- ]
136
- }),
137
- /*#__PURE__*/ jsx("div", {
138
- className: styles.contentList,
139
- children: siteConfig.discoveredContent.map((content)=>/*#__PURE__*/ jsxs("div", {
140
- className: styles.contentItem,
141
- children: [
142
- /*#__PURE__*/ jsxs("span", {
143
- className: styles.contentLabel,
144
- children: [
145
- content.label,
146
- ":"
147
- ]
148
- }),
149
- /*#__PURE__*/ jsxs("span", {
150
- className: styles.contentCount,
151
- children: [
152
- content.count,
153
- " items"
154
- ]
155
- }),
156
- content.custom && /*#__PURE__*/ jsx("span", {
157
- className: styles.customBadge,
158
- children: "Custom"
159
- })
160
- ]
161
- }, content.type))
162
- })
163
- ]
164
- }),
165
- siteConfig.connectionStatus === 'scanned' && 'Content Discovered',
166
- siteConfig.connectionStatus === 'failed' && 'Scan Failed'
167
- ]
168
- })
169
- ]
170
- })
171
44
  }),
172
45
  /*#__PURE__*/ jsx("div", {
173
- className: styles.formActions,
174
- children: configState.isEditing ? /*#__PURE__*/ jsxs(Fragment, {
175
- children: [
176
- /*#__PURE__*/ jsx("button", {
177
- className: styles.saveButton,
178
- disabled: loading,
179
- onClick: onSave,
180
- children: "Save Configuration"
181
- }),
182
- configState.isSaved && /*#__PURE__*/ jsx("button", {
183
- className: styles.cancelButton,
184
- disabled: loading,
185
- onClick: onCancel,
186
- children: "Cancel"
187
- })
188
- ]
189
- }) : /*#__PURE__*/ jsx("button", {
190
- className: styles.editButton,
191
- disabled: loading,
192
- onClick: onEdit,
193
- children: "Edit Configuration"
194
- })
46
+ className: styles.contentList,
47
+ children: siteConfig.discoveredContent.map((content)=>/*#__PURE__*/ jsxs("div", {
48
+ className: styles.contentItem,
49
+ children: [
50
+ /*#__PURE__*/ jsxs("span", {
51
+ className: styles.contentLabel,
52
+ children: [
53
+ content.label,
54
+ ":"
55
+ ]
56
+ }),
57
+ /*#__PURE__*/ jsxs("span", {
58
+ className: styles.contentCount,
59
+ children: [
60
+ content.count,
61
+ " items"
62
+ ]
63
+ }),
64
+ content.custom && /*#__PURE__*/ jsx("span", {
65
+ className: styles.customBadge,
66
+ children: "Custom"
67
+ })
68
+ ]
69
+ }, content.type))
195
70
  })
196
71
  ]
197
72
  })
198
- ]
73
+ })
74
+ }),
75
+ siteConfig.connectionStatus === 'failed' && /*#__PURE__*/ jsx("div", {
76
+ className: styles.siteConfigForm,
77
+ children: /*#__PURE__*/ jsxs("div", {
78
+ className: styles.connectionStatus,
79
+ children: [
80
+ /*#__PURE__*/ jsx("span", {
81
+ className: `${styles.statusDot} ${styles.failed}`
82
+ }),
83
+ /*#__PURE__*/ jsx("span", {
84
+ className: styles.statusText,
85
+ children: "Scan Failed — check that WordPress credentials are configured in the plugin config"
86
+ })
87
+ ]
88
+ })
199
89
  })
200
90
  ]
201
91
  });
@@ -1 +1 @@
1
- {"version":3,"file":"SiteConfigPanel.js","sources":["../../../src/components/dashboard/SiteConfigPanel.tsx"],"sourcesContent":["'use client'\n\nimport type { SiteConfigState, WordPressSiteConfig } from './types'\n\nimport styles from '../MigrationDashboardClient.module.css'\n\nexport const SiteConfigPanel = ({\n configState,\n loading,\n onCancel,\n onConfigChange,\n onEdit,\n onSave,\n onToggle,\n showSiteConfig,\n siteConfig,\n}: {\n configState: SiteConfigState\n loading: boolean\n onCancel: () => void\n onConfigChange: (field: keyof WordPressSiteConfig, value: string) => void\n onEdit: () => void\n onSave: () => void\n onToggle: () => void\n showSiteConfig: boolean\n siteConfig: WordPressSiteConfig\n}) => {\n return (\n <div className={styles.siteConfigSection}>\n <div className={styles.siteConfigHeader}>\n <h2>WordPress Site Configuration</h2>\n <button className={styles.toggleButton} onClick={onToggle}>\n {showSiteConfig ? 'Hide Configuration' : 'Show Configuration'}\n </button>\n </div>\n\n {showSiteConfig && (\n <div className={styles.siteConfigForm}>\n <div className={styles.formGrid}>\n <div className={styles.formGroup}>\n <label htmlFor=\"siteName\">Site Name</label>\n <input\n disabled={!configState.isEditing}\n id=\"siteName\"\n onChange={(e) => onConfigChange('siteName', e.target.value)}\n placeholder=\"My WordPress Site\"\n type=\"text\"\n value={siteConfig.siteName}\n />\n </div>\n\n <div className={styles.formGroup}>\n <label htmlFor=\"wpSiteUrl\">WordPress Site URL</label>\n <input\n disabled={!configState.isEditing}\n id=\"wpSiteUrl\"\n onChange={(e) => onConfigChange('wpSiteUrl', e.target.value)}\n placeholder=\"https://example.com\"\n type=\"url\"\n value={siteConfig.wpSiteUrl}\n />\n <small>Base URL of your WordPress site</small>\n </div>\n\n <div className={styles.formGroup}>\n <label htmlFor=\"wpUsername\">WordPress Username</label>\n <input\n disabled={!configState.isEditing}\n id=\"wpUsername\"\n onChange={(e) => onConfigChange('wpUsername', e.target.value)}\n placeholder=\"admin\"\n type=\"text\"\n value={siteConfig.wpUsername}\n />\n </div>\n\n <div className={styles.formGroup}>\n <label htmlFor=\"wpPassword\">Application Password</label>\n <input\n disabled={!configState.isEditing}\n id=\"wpPassword\"\n onChange={(e) => onConfigChange('wpPassword', e.target.value)}\n placeholder=\"xxxx xxxx xxxx xxxx\"\n type=\"password\"\n value={siteConfig.wpPassword}\n />\n <small>WordPress Application Password for REST API access</small>\n </div>\n </div>\n\n <div className={styles.configActions}>\n <div className={styles.connectionStatus}>\n <div className={styles.statusIndicator}>\n <span className={`${styles.statusDot} ${styles[siteConfig.connectionStatus]}`} />\n <span className={styles.statusText}>\n {siteConfig.connectionStatus === 'not-scanned' && 'Not Scanned'}\n {siteConfig.connectionStatus === 'scanned' &&\n siteConfig.discoveredContent &&\n siteConfig.discoveredContent.length > 0 && (\n <div className={styles.discoveredContent}>\n <div className={styles.discoveredHeader}>\n <h4>Available Content ({siteConfig.totalItems} total items)</h4>\n <small>Ready for migration</small>\n </div>\n <div className={styles.contentList}>\n {siteConfig.discoveredContent.map((content) => (\n <div className={styles.contentItem} key={content.type}>\n <span className={styles.contentLabel}>{content.label}:</span>\n <span className={styles.contentCount}>{content.count} items</span>\n {content.custom && (\n <span className={styles.customBadge}>Custom</span>\n )}\n </div>\n ))}\n </div>\n </div>\n )}\n {siteConfig.connectionStatus === 'scanned' && 'Content Discovered'}\n {siteConfig.connectionStatus === 'failed' && 'Scan Failed'}\n </span>\n </div>\n </div>\n\n <div className={styles.formActions}>\n {configState.isEditing ? (\n <>\n <button\n className={styles.saveButton}\n disabled={loading}\n onClick={onSave}\n >\n Save Configuration\n </button>\n {configState.isSaved && (\n <button\n className={styles.cancelButton}\n disabled={loading}\n onClick={onCancel}\n >\n Cancel\n </button>\n )}\n </>\n ) : (\n <button\n className={styles.editButton}\n disabled={loading}\n onClick={onEdit}\n >\n Edit Configuration\n </button>\n )}\n </div>\n </div>\n </div>\n )}\n </div>\n )\n}\n"],"names":["className","showSiteConfig","onChange","value","siteConfig","content","configState"],"mappings":";;;;AAMO;AAqBL;AACOA;;;AACEA;;;AACC;;;AACIA;;AACLC;;;;AAIJA;AACMD;;;AACEA;;;AACEA;;;;AACuB;;;;;AAIxBE;;;AAGAC;;;;;AAICH;;;;AACwB;;;;;AAIzBE;;;AAGAC;;;AAEK;;;;;AAGJH;;;;AACyB;;;;;AAI1BE;;;AAGAC;;;;;AAICH;;;;AACyB;;;;;AAI1BE;;;AAGAC;;;AAEK;;;;;;;AAINH;;;AACEA;AACH;AAAKA;;;;;;AAEGA;;;AAEHI;AAGQJ;;;AACEA;;;;AACC;AAAoBI;AAAsB;;;;AACvC;;;;;AAEJJ;AACFI;AACMJ;;;AACGA;;AAAiCK;AAAc;;;;AAC/CL;;AAAiCK;AAAc;;;;AAE7CL;AAA+B;;;AAJAK;;;;;;;;;;;;AAiBpDL;AACFM;;;AAGKN;;;AAGD;;;AAKGA;;;AAGD;;;;AAOHA;;;AAGD;;;;;;;;;AAUjB;;"}
1
+ {"version":3,"file":"SiteConfigPanel.js","sources":["../../../src/components/dashboard/SiteConfigPanel.tsx"],"sourcesContent":["'use client'\n\nimport type { WordPressSiteConfig } from './types'\n\nimport styles from '../MigrationDashboardClient.module.css'\n\nexport const SiteConfigPanel = ({\n loading,\n onScan,\n siteConfig,\n}: {\n loading: boolean\n onScan: () => void\n siteConfig: WordPressSiteConfig\n}) => {\n return (\n <div className={styles.siteConfigSection}>\n <div className={styles.siteConfigHeader}>\n <h2>WordPress Content Discovery</h2>\n <button\n className={styles.saveButton}\n disabled={loading || siteConfig.connectionStatus === 'scanning'}\n onClick={onScan}\n >\n {siteConfig.connectionStatus === 'scanning' ? 'Scanning...' : 'Scan WordPress Content'}\n </button>\n </div>\n\n {siteConfig.connectionStatus === 'scanned' &&\n siteConfig.discoveredContent &&\n siteConfig.discoveredContent.length > 0 && (\n <div className={styles.siteConfigForm}>\n <div className={styles.connectionStatus}>\n <div className={styles.discoveredContent}>\n <div className={styles.discoveredHeader}>\n <h4>Available Content ({siteConfig.totalItems} total items)</h4>\n <small>Ready for migration</small>\n </div>\n <div className={styles.contentList}>\n {siteConfig.discoveredContent.map((content) => (\n <div className={styles.contentItem} key={content.type}>\n <span className={styles.contentLabel}>{content.label}:</span>\n <span className={styles.contentCount}>{content.count} items</span>\n {content.custom && <span className={styles.customBadge}>Custom</span>}\n </div>\n ))}\n </div>\n </div>\n </div>\n </div>\n )}\n\n {siteConfig.connectionStatus === 'failed' && (\n <div className={styles.siteConfigForm}>\n <div className={styles.connectionStatus}>\n <span className={`${styles.statusDot} ${styles.failed}`} />\n <span className={styles.statusText}>\n Scan Failed — check that WordPress credentials are configured in the plugin config\n </span>\n </div>\n </div>\n )}\n </div>\n )\n}\n"],"names":["className","siteConfig","content"],"mappings":";;;;AAMO;AASL;AACOA;;;AACEA;;;AACC;;;AAEFA;;;;;;;AAQHC;AAGQD;AACH;AAAKA;AACH;AAAKA;;;AACEA;;;;AACC;AAAoBC;AAAsB;;;;AACvC;;;;;AAEJD;AACFC;AACMD;;;AACGA;;AAAiCE;AAAc;;;;AAC/CF;;AAAiCE;AAAc;;;;AAC5BF;AAA+B;;;AAHjBE;;;;;;;AAahDF;AACH;AAAKA;;;;;;AAEGA;AAA8B;;;;;;;AAQhD;;"}
@@ -4,4 +4,4 @@ export { LogViewer } from './LogViewer';
4
4
  export { SiteConfigPanel } from './SiteConfigPanel';
5
5
  export { StatsOverview } from './StatsOverview';
6
6
  export { useMigrationDashboard } from './useMigrationDashboard';
7
- export type { ContentType, JobActionType, MigrationSummary, SiteConfigState, WordPressSiteConfig, } from './types';
7
+ export type { ContentType, JobActionType, MigrationSummary, WordPressSiteConfig, } from './types';
@@ -33,14 +33,6 @@ export type ContentType = {
33
33
  export type WordPressSiteConfig = {
34
34
  connectionStatus: 'failed' | 'not-scanned' | 'scanned' | 'scanning';
35
35
  discoveredContent?: ContentType[];
36
- siteName: string;
37
36
  totalItems?: number;
38
- wpPassword: string;
39
- wpSiteUrl: string;
40
- wpUsername: string;
41
- };
42
- export type SiteConfigState = {
43
- isEditing: boolean;
44
- isSaved: boolean;
45
37
  };
46
38
  export type JobActionType = 'pause' | 'restart' | 'resume' | 'retry' | 'rollback' | 'start' | 'update';
@@ -1,15 +1,9 @@
1
- import type { JobActionType, MigrationSummary, SiteConfigState, WordPressSiteConfig } from './types';
1
+ import type { JobActionType, MigrationSummary, WordPressSiteConfig } from './types';
2
2
  export declare const useMigrationDashboard: (initialSummary: MigrationSummary) => {
3
- configState: SiteConfigState;
4
3
  loading: boolean;
5
- showSiteConfig: boolean;
6
4
  siteConfig: WordPressSiteConfig;
7
5
  summary: MigrationSummary;
8
- handleCancelEdit: () => void;
9
- handleEditConfig: () => void;
10
- handleSaveConfig: () => Promise<void>;
11
- handleSiteConfigChange: (field: keyof WordPressSiteConfig, value: string) => void;
12
- setShowSiteConfig: import("react").Dispatch<import("react").SetStateAction<boolean>>;
13
6
  handleJobAction: (action: JobActionType, jobId: string) => Promise<void>;
7
+ scanWordPressContent: () => Promise<void>;
14
8
  getAllLogs: () => string[];
15
9
  };