UNPKG

payload-wordpress-migrator

Version:

A PayloadCMS plugin for WordPress migration - migrate and manage WordPress content directly in your Payload admin dashboard

436 lines (435 loc) 19.7 kB
'use client'; import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import { useField } from '@payloadcms/ui'; import React, { useEffect, useRef, useState } from 'react'; const SimpleFieldMapping = ({ path })=>{ const { setValue, value } = useField({ path }); const { value: contentTypeValue } = useField({ path: 'contentType' }); const { value: targetCollectionValue } = useField({ path: 'targetCollection' }); // Use ref to prevent infinite loops - change to any type to handle different value types const isUpdatingRef = useRef(false); const lastValueRef = useRef(''); // Dynamic field options from API const [sourceFields, setSourceFields] = useState([]); const [destinationFields, setDestinationFields] = useState([]); const [fieldMappings, setFieldMappings] = useState([ { destinationField: '', sourceField: '' } ]); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); // Fetch WordPress content fields const fetchWordPressFields = async (contentType)=>{ try { setLoading(true); setError(''); const savedConfig = localStorage.getItem('wp-site-config'); if (!savedConfig) { throw new Error('WordPress site configuration not found'); } const config = JSON.parse(savedConfig); const requestBody = { contentType, wpPassword: config.wpPassword, wpSiteUrl: config.wpSiteUrl, wpUsername: config.wpUsername }; const response = await fetch('/api/wordpress/content-fields', { body: JSON.stringify(requestBody), headers: { 'Content-Type': 'application/json' }, method: 'POST' }); const result = await response.json(); if (response.ok && result.success) { const wpFieldOptions = (result.fields || []).map((field)=>({ type: field.type, label: `${field.label} (${field.type})${field.custom ? ' *' : ''}`, path: field.path || field.name, value: field.path || field.name })); setSourceFields(wpFieldOptions); } else { console.error('API returned error:', result); throw new Error(result.error || 'Failed to fetch WordPress fields'); } } catch (error) { console.error('Error fetching WordPress fields:', error); setError(error instanceof Error ? error.message : 'Failed to fetch WordPress fields'); setSourceFields([]); } }; // Fetch PayloadCMS collection fields const fetchPayloadFields = async (collectionSlug)=>{ try { const response = await fetch(`/api/collections/${collectionSlug}/fields`); const result = await response.json(); if (response.ok && result.success) { const payloadFieldOptions = (result.fields || []).map((field)=>({ type: field.type, label: `${field.label} (${field.type})`, path: field.path || field.name, value: field.path || field.name })); setDestinationFields(payloadFieldOptions); } else { console.error('Payload API returned error:', result); throw new Error(result.error || 'Failed to fetch Payload collection fields'); } } catch (error) { console.error('Error fetching Payload fields:', error); setDestinationFields([]); } }; // Fetch fields when both content type and target collection are selected useEffect(()=>{ const fetchFields = async ()=>{ if (contentTypeValue && targetCollectionValue) { setLoading(true); try { await Promise.all([ fetchWordPressFields(contentTypeValue), fetchPayloadFields(targetCollectionValue) ]); } catch (error) { console.error('Error fetching fields:', error); } finally{ setLoading(false); } } else { setSourceFields([]); setDestinationFields([]); setFieldMappings([ { destinationField: '', sourceField: '' } ]); setError(''); } }; void fetchFields(); }, [ contentTypeValue, targetCollectionValue ]); // Parse existing field mappings from JSON value - only when value actually changes useEffect(()=>{ if (isUpdatingRef.current) { return; } // Prevent processing our own updates // Convert value to string for comparison const currentValue = typeof value === 'string' ? value : JSON.stringify(value || ''); if (value && currentValue !== lastValueRef.current) { try { const parsed = typeof value === 'string' ? JSON.parse(value) : value; if (parsed && Array.isArray(parsed.fieldMappings)) { setFieldMappings(parsed.fieldMappings); } lastValueRef.current = currentValue; } catch (error) { console.warn('Failed to parse field mappings:', error); } } }, [ value ]); // Function to update parent value without causing loops const updateParentValue = (mappings)=>{ const newValue = JSON.stringify({ contentType: contentTypeValue, fieldMappings: mappings, targetCollection: targetCollectionValue }); // Prevent infinite loops if (newValue !== lastValueRef.current && !isUpdatingRef.current) { isUpdatingRef.current = true; setValue(newValue); lastValueRef.current = newValue; // Reset the flag after a short delay setTimeout(()=>{ isUpdatingRef.current = false; }, 100); } }; // Add new mapping row const handleAddMapping = ()=>{ const newMappings = [ ...fieldMappings, { destinationField: '', sourceField: '' } ]; setFieldMappings(newMappings); updateParentValue(newMappings); }; // Remove mapping row const handleRemoveMapping = (idx)=>{ if (fieldMappings.length <= 1) { return; } // Keep at least one mapping const newMappings = fieldMappings.filter((_, i)=>i !== idx); setFieldMappings(newMappings); updateParentValue(newMappings); }; // Update mapping const handleChange = (idx, field, newValue)=>{ const newMappings = fieldMappings.map((mapping, i)=>i === idx ? { ...mapping, [field]: newValue } : mapping); setFieldMappings(newMappings); updateParentValue(newMappings); }; // Filter dropdown options to exclude already-mapped fields (except for current row) const getFilteredOptions = (options, field, idx)=>{ const used = fieldMappings.map((m, i)=>i !== idx ? m[field] : null).filter(Boolean); return options.filter((opt)=>!used.includes(opt.value)); }; if (!contentTypeValue || !targetCollectionValue) { return /*#__PURE__*/ _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: '1rem' }, children: [ /*#__PURE__*/ _jsx("label", { style: { color: 'var(--theme-elevation-700)', fontSize: '14px', fontWeight: '600' }, children: "Field Mapping Configuration" }), /*#__PURE__*/ _jsx("div", { style: { backgroundColor: 'var(--theme-elevation-50)', border: '1px solid var(--theme-elevation-200)', borderRadius: '4px', color: 'var(--theme-elevation-600)', padding: '1rem', textAlign: 'center' }, children: 'Please select both "Content Type to Migrate" and "Target Payload Collection" to configure field mappings.' }) ] }); } return /*#__PURE__*/ _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: '1rem' }, children: [ /*#__PURE__*/ _jsxs("div", { style: { alignItems: 'center', display: 'flex', justifyContent: 'space-between' }, children: [ /*#__PURE__*/ _jsx("label", { style: { color: 'var(--theme-elevation-700)', fontSize: '14px', fontWeight: '600' }, children: "Field Mapping Configuration" }), /*#__PURE__*/ _jsx("button", { disabled: loading, onClick: handleAddMapping, style: { backgroundColor: loading ? 'var(--theme-elevation-200)' : 'var(--theme-success-600)', border: 'none', borderRadius: '4px', color: loading ? 'var(--theme-elevation-500)' : 'white', cursor: loading ? 'not-allowed' : 'pointer', fontSize: '12px', fontWeight: '500', padding: '0.5rem 1rem' }, type: "button", children: "Add Mapping" }) ] }), loading && /*#__PURE__*/ _jsx("div", { style: { backgroundColor: 'var(--theme-elevation-50)', border: '1px solid var(--theme-elevation-200)', borderRadius: '4px', color: 'var(--theme-elevation-600)', padding: '1rem', textAlign: 'center' }, children: "Loading field schemas from WordPress and PayloadCMS..." }), error && /*#__PURE__*/ _jsxs("div", { style: { backgroundColor: 'var(--theme-error-50)', border: '1px solid var(--theme-error-200)', borderRadius: '4px', color: 'var(--theme-error-700)', padding: '1rem' }, children: [ /*#__PURE__*/ _jsx("strong", { children: "Error:" }), " ", error ] }), !loading && !error && sourceFields.length === 0 && destinationFields.length === 0 && /*#__PURE__*/ _jsx("div", { style: { backgroundColor: 'var(--theme-elevation-50)', border: '1px solid var(--theme-elevation-200)', borderRadius: '4px', color: 'var(--theme-elevation-600)', padding: '1rem', textAlign: 'center' }, children: "No fields found. Please check your WordPress connection and selected content type." }), !loading && !error && (sourceFields.length > 0 || destinationFields.length > 0) && /*#__PURE__*/ _jsxs(_Fragment, { children: [ fieldMappings.length === 0 && /*#__PURE__*/ _jsx("div", { style: { backgroundColor: 'var(--theme-elevation-50)', border: '1px solid var(--theme-elevation-200)', borderRadius: '4px', color: 'var(--theme-elevation-600)', padding: '1rem', textAlign: 'center' }, children: 'No field mappings configured. Click "Add Mapping" to start.' }), fieldMappings.map((mapping, idx)=>/*#__PURE__*/ _jsxs("div", { style: { alignItems: 'center', backgroundColor: 'var(--theme-elevation-0)', border: '1px solid var(--theme-elevation-200)', borderRadius: '4px', display: 'grid', gap: '0.75rem', gridTemplateColumns: '1fr auto 1fr auto', padding: '1rem' }, children: [ /*#__PURE__*/ _jsxs("select", { onChange: (e)=>handleChange(idx, 'sourceField', e.target.value), style: { backgroundColor: 'var(--theme-elevation-0)', border: '1px solid var(--theme-elevation-200)', borderRadius: '4px', fontSize: '14px', padding: '0.5rem' }, value: mapping.sourceField, children: [ /*#__PURE__*/ _jsx("option", { value: "", children: "Select WordPress field..." }), getFilteredOptions(sourceFields, 'sourceField', idx).map((opt, optIdx)=>/*#__PURE__*/ _jsx("option", { value: opt.value, children: opt.label }, `source-${idx}-${opt.value}-${optIdx}`)) ] }), /*#__PURE__*/ _jsx("span", { style: { color: 'var(--theme-elevation-400)', fontSize: '18px', fontWeight: 'bold' }, children: "→" }), /*#__PURE__*/ _jsxs("select", { onChange: (e)=>handleChange(idx, 'destinationField', e.target.value), style: { backgroundColor: 'var(--theme-elevation-0)', border: '1px solid var(--theme-elevation-200)', borderRadius: '4px', fontSize: '14px', padding: '0.5rem' }, value: mapping.destinationField, children: [ /*#__PURE__*/ _jsx("option", { value: "", children: "Select Payload field..." }), getFilteredOptions(destinationFields, 'destinationField', idx).map((opt, optIdx)=>/*#__PURE__*/ _jsx("option", { value: opt.value, children: opt.label }, `dest-${idx}-${opt.value}-${optIdx}`)) ] }), /*#__PURE__*/ _jsx("button", { disabled: fieldMappings.length <= 1, onClick: ()=>handleRemoveMapping(idx), style: { backgroundColor: fieldMappings.length <= 1 ? 'var(--theme-elevation-200)' : 'var(--theme-error-600)', border: 'none', borderRadius: '4px', color: fieldMappings.length <= 1 ? 'var(--theme-elevation-500)' : 'white', cursor: fieldMappings.length <= 1 ? 'not-allowed' : 'pointer', fontSize: '12px', fontWeight: '500', minWidth: '60px', padding: '0.5rem' }, type: "button", children: "Remove" }) ] }, idx)) ] }), /*#__PURE__*/ _jsxs("div", { style: { color: 'var(--theme-elevation-500)', fontSize: '12px', fontStyle: 'italic', marginTop: '0.5rem' }, children: [ "Map WordPress fields to their corresponding Payload fields. Each field can only be mapped once. Fields marked with * are custom fields (ACF/Meta).", sourceFields.length > 0 && /*#__PURE__*/ _jsxs(_Fragment, { children: [ /*#__PURE__*/ _jsx("br", {}), /*#__PURE__*/ _jsx("strong", { children: "Available WordPress fields:" }), " ", sourceFields.length, " |", ' ', /*#__PURE__*/ _jsx("strong", { children: "Available Payload fields:" }), " ", destinationFields.length ] }) ] }) ] }); }; export default SimpleFieldMapping; //# sourceMappingURL=SimpleFieldMapping.js.map