/** * Automation Dashboard Page * Main page for managing AI automation pipeline */ import React, { useState, useEffect } from 'react'; import { useToast } from '../../components/ui/toast/ToastContainer'; import { useSiteStore } from '../../store/siteStore'; import { automationService, AutomationRun, AutomationConfig, PipelineStage } from '../../services/automationService'; import StageCard from '../../components/Automation/StageCard'; import ActivityLog from '../../components/Automation/ActivityLog'; import ConfigModal from '../../components/Automation/ConfigModal'; import RunHistory from '../../components/Automation/RunHistory'; import PageMeta from '../../components/common/PageMeta'; import ComponentCard from '../../components/common/ComponentCard'; import DebugSiteSelector from '../../components/common/DebugSiteSelector'; import Button from '../../components/ui/button/Button'; import { BoltIcon } from '../../icons'; const STAGE_NAMES = [ 'Keywords → Clusters', 'Clusters → Ideas', 'Ideas → Tasks', 'Tasks → Content', 'Content → Image Prompts', 'Image Prompts → Images', 'Manual Review Gate', ]; const AutomationPage: React.FC = () => { const { activeSite } = useSiteStore(); const toast = useToast(); const [config, setConfig] = useState(null); const [currentRun, setCurrentRun] = useState(null); const [pipelineOverview, setPipelineOverview] = useState([]); const [showConfigModal, setShowConfigModal] = useState(false); const [loading, setLoading] = useState(true); const [lastUpdated, setLastUpdated] = useState(new Date()); const [estimate, setEstimate] = useState<{ estimated_credits: number; current_balance: number; sufficient: boolean } | null>(null); // Poll for current run updates useEffect(() => { if (!activeSite) return; loadData(); // Poll every 5 seconds when run is active const interval = setInterval(() => { if (currentRun && (currentRun.status === 'running' || currentRun.status === 'paused')) { loadCurrentRun(); } else { // Refresh pipeline overview when not running loadPipelineOverview(); } }, 5000); return () => clearInterval(interval); }, [activeSite, currentRun?.status]); const loadData = async () => { if (!activeSite) return; try { setLoading(true); const [configData, runData, estimateData, pipelineData] = await Promise.all([ automationService.getConfig(activeSite.id), automationService.getCurrentRun(activeSite.id), automationService.estimate(activeSite.id), automationService.getPipelineOverview(activeSite.id), ]); setConfig(configData); setCurrentRun(runData.run); setEstimate(estimateData); setPipelineOverview(pipelineData.stages); setLastUpdated(new Date()); } catch (error: any) { toast.error('Failed to load automation data'); console.error(error); } finally { setLoading(false); } }; const loadCurrentRun = async () => { if (!activeSite) return; try { const data = await automationService.getCurrentRun(activeSite.id); setCurrentRun(data.run); } catch (error) { console.error('Failed to poll current run', error); } }; const loadPipelineOverview = async () => { if (!activeSite) return; try { const data = await automationService.getPipelineOverview(activeSite.id); setPipelineOverview(data.stages); } catch (error) { console.error('Failed to poll pipeline overview', error); } }; const handleRunNow = async () => { if (!activeSite) return; // Check credit balance if (estimate && !estimate.sufficient) { toast.error(`Insufficient credits. Need ~${estimate.estimated_credits}, you have ${estimate.current_balance}`); return; } try { const result = await automationService.runNow(activeSite.id); toast.success('Automation started'); loadCurrentRun(); } catch (error: any) { toast.error(error.response?.data?.error || 'Failed to start automation'); } }; const handlePause = async () => { if (!currentRun) return; try { await automationService.pause(currentRun.run_id); toast.success('Automation paused'); loadCurrentRun(); } catch (error) { toast.error('Failed to pause automation'); } }; const handleResume = async () => { if (!currentRun) return; try { await automationService.resume(currentRun.run_id); toast.success('Automation resumed'); loadCurrentRun(); } catch (error) { toast.error('Failed to resume automation'); } }; const handleSaveConfig = async (newConfig: Partial) => { if (!activeSite) return; try { await automationService.updateConfig(activeSite.id, newConfig); toast.success('Configuration saved'); setShowConfigModal(false); loadData(); } catch (error) { toast.error('Failed to save configuration'); } }; if (loading) { return (
Loading automation...
); } if (!activeSite) { return (
Please select a site to view automation
); } return ( <>
{/* Page Header with Site Selector (no sector) */}

AI Automation Pipeline

{activeSite && (
{lastUpdated && (

Last updated: {lastUpdated.toLocaleTimeString()}

)}

Site: {activeSite.name}

)}
{/* Schedule Status Card */} {config && (
Status
{config.is_enabled ? ( ● Enabled ) : ( ○ Disabled )}
Schedule
{config.frequency} at {config.scheduled_time}
Last Run
{config.last_run_at ? new Date(config.last_run_at).toLocaleString() : 'Never'}
Estimated Credits
{estimate?.estimated_credits || 0} credits {estimate && !estimate.sufficient && ( (Insufficient) )}
{currentRun?.status === 'running' && ( )} {currentRun?.status === 'paused' && ( )} {!currentRun && ( )}
)} {/* Pipeline Overview - Always Visible */}
{currentRun ? ( <> ● Live Run Active - Stage {currentRun.current_stage} of 7 ) : ( <> Pipeline Status - Ready to run )}
{pipelineOverview.reduce((sum, stage) => sum + stage.pending, 0)} total items pending
{/* Stage Cards Grid */}
{STAGE_NAMES.map((name, index) => ( ))}
{/* Current Run Status */} {currentRun && (
Status
{currentRun.status === 'running' && ● {currentRun.status}} {currentRun.status === 'paused' && ⏸ {currentRun.status}} {currentRun.status === 'completed' && ✓ {currentRun.status}} {currentRun.status === 'failed' && ✗ {currentRun.status}}
Current Stage
Stage {currentRun.current_stage}: {STAGE_NAMES[currentRun.current_stage - 1]}
Started
{new Date(currentRun.started_at).toLocaleString()}
Credits Used
{currentRun.total_credits_used}
)} {/* Activity Log */} {currentRun && ( )} {/* Run History */} {/* Config Modal */} {showConfigModal && config && ( setShowConfigModal(false)} /> )}
); }; export default AutomationPage;