/** * WordPress dependencies */ import { __ } from '@wordpress/i18n'; import { Button, Modal, TextControl, ColorPicker, Card, CardBody, Popover, } from '@wordpress/components'; import { useState, useRef } from '@wordpress/element'; import { DndProvider, useDrag, useDrop } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; import apiFetch from '@wordpress/api-fetch'; const ItemTypes = { DATASET_ITEM: 'dataset_item' }; const DatasetItem = ({ item, datasetKey, moveItem, updateItem, items, onDelete }) => { const [showColorPicker, setShowColorPicker] = useState(false); const ref = useRef(null); const [{ isDragging }, drag] = useDrag({ type: ItemTypes.DATASET_ITEM, item: { id: item.id, datasetKey }, collect: (monitor) => ({ isDragging: monitor.isDragging(), }), }); const [{ isOver }, drop] = useDrop({ accept: ItemTypes.DATASET_ITEM, hover(draggedItem, monitor) { if (!ref.current) return; if (draggedItem.id === item.id) return; const hoverBoundingRect = ref.current.getBoundingClientRect(); const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2; const hoverMiddleX = (hoverBoundingRect.right - hoverBoundingRect.left) / 2; const clientOffset = monitor.getClientOffset(); if (!clientOffset) return; const hoverClientY = clientOffset.y - hoverBoundingRect.top; const hoverClientX = clientOffset.x - hoverBoundingRect.left; // If hovering in the right half and below middle, make it a child const shouldBeChild = hoverClientX > hoverMiddleX && hoverClientY > hoverMiddleY; moveItem(draggedItem.id, item.id, shouldBeChild); }, collect: (monitor) => ({ isOver: monitor.isOver(), }), }); // Initialize drag and drop refs drag(drop(ref)); // Get child items const childItems = items.filter(i => i.parent === item.id); // Calculate the nesting level const getItemDepth = (itemId) => { let depth = 0; let currentItem = items.find(i => i.id === itemId); while (currentItem && currentItem.parent) { depth++; currentItem = items.find(i => i.id === currentItem.parent); } return depth; }; const depth = getItemDepth(item.id); const style = { marginLeft: `${depth * 20}px`, // 20px indentation per level opacity: isDragging ? 0.5 : 1, cursor: 'move', background: isOver ? '#f0f0f0' : 'white', border: isOver ? '2px dashed #0073aa' : '1px solid #e2e4e7', marginBottom: '8px', transition: 'all 0.2s ease', }; return (
updateItem(datasetKey, item.id, 'label', value)} style={{ flex: 2 }} /> updateItem(datasetKey, item.id, 'value', parseFloat(value))} style={{ flex: 1 }} />
{/* Render child items */} {childItems.map(childItem => ( ))}
); }; const LCPDatasetBuilder = ({ value, onChange }) => { const [isModalOpen, setIsModalOpen] = useState(false); const [datasets, setDatasets] = useState(value || {}); const [isSaving, setIsSaving] = useState(false); const [editingDataset, setEditingDataset] = useState(null); const addNewDataset = () => { const defaultName = 'New Dataset'; let newName = defaultName; let counter = 1; // Ensure unique name while (datasets[newName]) { newName = `${defaultName} ${counter}`; counter++; } const updatedDatasets = { ...datasets, [newName]: [{ id: `dataset-${Date.now()}`, label: 'New Item', parent: null, value: 0, color: '#000000' }] }; setDatasets(updatedDatasets); onChange(updatedDatasets); setEditingDataset(newName); }; const renameDataset = (oldName, newName) => { if (oldName === newName || !newName.trim() || datasets[newName]) { setEditingDataset(null); return; } const updatedDatasets = { ...datasets }; updatedDatasets[newName] = updatedDatasets[oldName]; delete updatedDatasets[oldName]; setDatasets(updatedDatasets); onChange(updatedDatasets); setEditingDataset(null); }; const deleteDataset = (datasetKey) => { const updatedDatasets = { ...datasets }; delete updatedDatasets[datasetKey]; setDatasets(updatedDatasets); onChange(updatedDatasets); }; const deleteItem = (datasetKey, itemId) => { const updatedDatasets = { ...datasets }; // Remove the item and its children const removeItemAndChildren = (items, targetId) => { return items.filter(item => { if (item.id === targetId) return false; if (item.parent === targetId) return false; return true; }); }; updatedDatasets[datasetKey] = removeItemAndChildren(updatedDatasets[datasetKey], itemId); setDatasets(updatedDatasets); onChange(updatedDatasets); }; const addNewItem = (datasetKey) => { const newItem = { id: `item-${Date.now()}`, label: 'New Item', parent: null, value: 0, color: '#000000' }; const updatedDatasets = { ...datasets, [datasetKey]: [...datasets[datasetKey], newItem] }; setDatasets(updatedDatasets); onChange(updatedDatasets); }; const updateItem = (datasetKey, itemId, field, value) => { const updatedDatasets = { ...datasets, [datasetKey]: datasets[datasetKey].map(item => item.id === itemId ? { ...item, [field]: value } : item ) }; setDatasets(updatedDatasets); onChange(updatedDatasets); }; const moveItem = (draggedId, targetId, makeChild) => { const datasetKey = Object.keys(datasets).find(key => datasets[key].some(item => item.id === draggedId) ); if (!datasetKey) return; const items = [...datasets[datasetKey]]; const draggedItem = items.find(item => item.id === draggedId); const targetItem = items.find(item => item.id === targetId); if (!draggedItem || !targetItem) return; // If target is a descendant of dragged item, prevent the move let current = targetItem; while (current.parent) { if (current.parent === draggedId) return; current = items.find(item => item.id === current.parent); } // Update parent reference draggedItem.parent = makeChild ? targetId : targetItem.parent; const updatedDatasets = { ...datasets, [datasetKey]: items }; setDatasets(updatedDatasets); onChange(updatedDatasets); }; const saveToCollection = async () => { try { setIsSaving(true); console.log('Chart data:', datasets); const formattedDatasets = Object.entries(datasets).map(([name, data]) => ({ dataset_name: name, dataset_source: 'json', dataset_json: data })); console.log('Formatted datasets:', formattedDatasets); // Create the post first const postResponse = await apiFetch({ path: '/wp/v2/lcp-data-collection', method: 'POST', data: { title: Object.keys(datasets)[0] || 'Dataset Collection', status: 'publish', meta: { lcp_datasets: formattedDatasets } } }); console.log('Post created:', postResponse); console.log('Meta data in response:', postResponse.meta); // Double-check the meta was saved by fetching the post const savedPost = await apiFetch({ path: `/wp/v2/lcp-data-collection/${postResponse.id}`, method: 'GET' }); console.log('Saved post data:', savedPost); console.log('Saved meta data:', savedPost.meta?.lcp_datasets); } catch (error) { console.error('Error saving dataset:', error); console.log('Error details:', { message: error.message, code: error.code, data: error.data }); } finally { setIsSaving(false); } }; return ( <> {isModalOpen && ( setIsModalOpen(false)} style={{ width: '100%', maxWidth: '800px' }} >
{Object.entries(datasets).map(([datasetKey, items]) => (
{editingDataset === datasetKey ? ( renameDataset(datasetKey, newName)} onKeyDown={(e) => { if (e.key === 'Enter') { renameDataset(datasetKey, e.target.value); } else if (e.key === 'Escape') { setEditingDataset(null); } }} autoFocus /> ) : ( <>

{datasetKey}

)}
{items .filter(item => !item.parent) .map(item => ( deleteItem(datasetKey, itemId)} /> ))}
))}
)} ); }; export default LCPDatasetBuilder;