import React, { useState, useEffect, useRef } from 'react'; import Creator from './components/Creator'; import SupportModal from './components/SupportModal'; import TemplatesModal from './components/TemplatesModal'; const App = () => { const [view, setView] = useState('list'); const [workflows, setWorkflows] = useState([]); const [selectedWorkflow, setSelectedWorkflow] = useState(null); const [loading, setLoading] = useState(true); const [isSupportOpen, setIsSupportOpen] = useState(false); const [isTemplatesModalOpen, setIsTemplatesModalOpen] = useState(false); const [importStatus, setImportStatus] = useState(null); // { type: 'success'|'error', message: '' } const importFileRef = useRef(null); useEffect(() => { fetchWorkflows(); }, []); const fetchWorkflows = async () => { setLoading(true); try { const response = await fetch(`${window.wpbotAutomator.apiUrl}/workflows`, { headers: { 'X-WP-Nonce': window.wpbotAutomator.nonce, }, }); const data = await response.json(); setWorkflows(data || []); } catch (error) { console.error('Error fetching workflows:', error); } finally { setLoading(false); } }; const handleCreateNew = () => { setIsTemplatesModalOpen(true); }; const handleTemplateSelect = (template) => { setIsTemplatesModalOpen(false); if (template) { // Create a dummy workflow object from template setSelectedWorkflow({ id: null, name: template.name, description: template.description, workflow_data: template.workflow_data, status: 'active' }); } else { setSelectedWorkflow(null); } setView('creator'); }; const handleEdit = (workflow) => { setSelectedWorkflow(workflow); setView('creator'); }; const handleDelete = async (e, id) => { e.stopPropagation(); if (!window.confirm('Are you sure you want to delete this workflow?')) { return; } try { const response = await fetch(`${window.wpbotAutomator.apiUrl}/workflows/${id}`, { method: 'DELETE', headers: { 'X-WP-Nonce': window.wpbotAutomator.nonce, }, }); const data = await response.json(); if (data.success) { fetchWorkflows(); } else { alert('Failed to delete workflow.'); } } catch (error) { console.error('Error deleting workflow:', error); } }; const handleToggleStatus = async (e, workflow) => { e.stopPropagation(); const newStatus = workflow.status === 'active' ? 'inactive' : 'active'; try { const response = await fetch(`${window.wpbotAutomator.apiUrl}/workflows/${workflow.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': window.wpbotAutomator.nonce, }, body: JSON.stringify({ ...workflow, status: newStatus, }), }); const data = await response.json(); if (data.success) { fetchWorkflows(); } else { alert('Failed to update workflow status.'); } } catch (error) { console.error('Error updating workflow status:', error); } }; const handleBack = () => { setView('list'); fetchWorkflows(); }; // ── Export helpers ──────────────────────────────────────────────────────── /** * Trigger a JSON file download from a plain-object payload. */ const downloadJson = (data, filename) => { const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; a.click(); URL.revokeObjectURL(url); }; /** * Export a single workflow by ID. */ const handleExportSingle = async (e, workflow) => { e.stopPropagation(); try { const response = await fetch( `${window.wpbotAutomator.apiUrl}/workflows/export?id=${workflow.id}`, { headers: { 'X-WP-Nonce': window.wpbotAutomator.nonce } } ); const data = await response.json(); const safeName = workflow.name.replace(/[^a-z0-9_-]/gi, '_').toLowerCase(); downloadJson(data, `wpbot-workflow-${safeName}.json`); } catch (error) { console.error('Error exporting workflow:', error); alert('Failed to export workflow.'); } }; /** * Export all workflows. */ const handleExportAll = async () => { try { const response = await fetch( `${window.wpbotAutomator.apiUrl}/workflows/export`, { headers: { 'X-WP-Nonce': window.wpbotAutomator.nonce } } ); const data = await response.json(); downloadJson(data, 'wpbot-workflows-all.json'); } catch (error) { console.error('Error exporting all workflows:', error); alert('Failed to export workflows.'); } }; // ── Import helpers ──────────────────────────────────────────────────────── const handleImportClick = () => { setImportStatus(null); importFileRef.current && importFileRef.current.click(); }; const handleImportFile = (e) => { const file = e.target.files && e.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = async (evt) => { let parsed; try { parsed = JSON.parse(evt.target.result); } catch { setImportStatus({ type: 'error', message: 'Invalid JSON file. Please select a valid export file.' }); return; } if (!parsed.workflows || !Array.isArray(parsed.workflows)) { setImportStatus({ type: 'error', message: 'Invalid file format. Expected a "workflows" array.' }); return; } try { const response = await fetch(`${window.wpbotAutomator.apiUrl}/workflows/import`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': window.wpbotAutomator.nonce, }, body: JSON.stringify({ workflows: parsed.workflows }), }); const data = await response.json(); if (data.success || data.imported > 0) { const msg = `Successfully imported ${data.imported} workflow${data.imported !== 1 ? 's' : ''}.` + (data.errors && data.errors.length ? ` (${data.errors.length} skipped)` : ''); setImportStatus({ type: 'success', message: msg }); fetchWorkflows(); } else { const errMsg = (data.errors && data.errors.join(' ')) || 'Import failed. No workflows were imported.'; setImportStatus({ type: 'error', message: errMsg }); } } catch (error) { console.error('Error importing workflows:', error); setImportStatus({ type: 'error', message: 'Network error during import. Please try again.' }); } }; reader.readAsText(file); // Reset so the same file can be selected again e.target.value = ''; }; if (view === 'creator') { return ; } return (
{/* Import status message */} {importStatus && (
{importStatus.message}
)}

Workflows

{/* Hidden file input for import */} {/* Import button */} {/* Export All button — only shown when workflows exist */} {workflows.length > 0 && ( )} {/* Create New button */}
{loading ? (
Loading workflows...
) : workflows.length === 0 ? (
No workflows found. Create your first one!
) : (
{workflows.map((workflow) => (
handleEdit(workflow)} >

{workflow.name}

{/* Toggle active/inactive */} {/* Export single workflow */} {/* Delete */}

{workflow.description || 'No description'}

{workflow.status.charAt(0).toUpperCase() + workflow.status.slice(1)} ID: {workflow.id}
))}
)}
setIsSupportOpen(false)} /> setIsTemplatesModalOpen(false)} onSelect={handleTemplateSelect} isProActive={!!window.wpbotAutomator?.isProActive} />
); }; export default App;