/** * Automation Run Detail Page * Comprehensive view of a single automation run */ import React, { useEffect, useRef, useState } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { useSiteStore } from '../../store/siteStore'; import { automationService } from '../../services/automationService'; import { RunDetailResponse } from '../../types/automation'; import { useToast } from '../../components/ui/toast/ToastContainer'; import PageMeta from '../../components/common/PageMeta'; import PageHeader from '../../components/common/PageHeader'; import RunSummaryCard from '../../components/Automation/DetailView/RunSummaryCard'; import StageAccordion from '../../components/Automation/DetailView/StageAccordion'; import EfficiencyMetrics from '../../components/Automation/DetailView/EfficiencyMetrics'; import InsightsPanel from '../../components/Automation/DetailView/InsightsPanel'; import CreditBreakdownChart from '../../components/Automation/DetailView/CreditBreakdownChart'; const AutomationRunDetail: React.FC = () => { const { runId } = useParams<{ runId: string }>(); const navigate = useNavigate(); const { activeSite } = useSiteStore(); const { error: toastError } = useToast(); const [loading, setLoading] = useState(true); const [runDetail, setRunDetail] = useState(null); const [error, setError] = useState(null); const lastRequestKey = useRef(null); const decodeTitle = (value: string | undefined | null) => { if (!value) return ''; try { return decodeURIComponent(value); } catch { return value; } }; const getDisplayTitle = () => { const run = runDetail?.run; if (!run) return 'Automation Run'; if (run.site_name) return run.site_name; if (run.site_domain) return run.site_domain.replace('www.', ''); const decoded = decodeTitle(run.run_title); if (decoded) return decoded; if (run.run_number) return `Run #${run.run_number}`; return 'Automation Run'; }; useEffect(() => { const loadRunDetail = async () => { if (!runId) { setError('Missing run id'); setLoading(false); return; } if (!activeSite) { setError('Please select a site to view automation run details.'); setLoading(false); return; } const requestKey = `${activeSite.id}-${runId}`; if (lastRequestKey.current === requestKey) { return; } lastRequestKey.current = requestKey; try { setLoading(true); setError(null); const data = await automationService.getRunDetail(activeSite.id, runId); setRunDetail(data); } catch (err: any) { console.error('Failed to load run detail', err); const message = err?.message === 'Internal server error' ? 'Run detail is temporarily unavailable (server error). Please try again later.' : err?.message || 'Failed to load run detail'; setError(message); toastError(message); lastRequestKey.current = null; } finally { setLoading(false); } }; loadRunDetail(); }, [runId, activeSite, toastError]); if (!activeSite) { return (

Please select a site to view automation run details.

); } if (loading) { return (
Loading run details...
); } if (error) { return (

{error}

); } if (!runDetail) { return (

Run not found.

); } const displayTitle = getDisplayTitle(); const breadcrumbLabel = runDetail.run?.run_number ? `Run #${runDetail.run.run_number}` : displayTitle; const normalizedRun = runDetail.run ? { ...runDetail.run, run_title: displayTitle } : null; const stageSummary = (runDetail.stages || []).reduce( (acc, stage) => { acc.itemsProcessed += stage.items_processed || 0; acc.itemsCreated += stage.items_created || 0; if (stage.stage_number === 4) acc.contentCreated += stage.items_created || 0; if (stage.stage_number === 6) acc.imagesGenerated += stage.items_created || 0; return acc; }, { itemsProcessed: 0, itemsCreated: 0, contentCreated: 0, imagesGenerated: 0 } ); const derivedInsights = [] as RunDetailResponse['insights']; if (normalizedRun) { if ((normalizedRun.total_credits_used || 0) > 0 && stageSummary.itemsCreated === 0) { derivedInsights.push({ type: 'warning', severity: 'warning', message: 'Credits were spent but no outputs were recorded. Review stage errors and retry failed steps.', }); } if (normalizedRun.status === 'running') { derivedInsights.push({ type: 'success', severity: 'info', message: `Run is currently active in stage ${normalizedRun.current_stage || 1}.`, }); } } if ((runDetail.stages || []).some(stage => stage.status === 'failed')) { const failedStage = runDetail.stages.find(stage => stage.status === 'failed'); derivedInsights.push({ type: 'error', severity: 'error', message: `Stage ${failedStage?.stage_number} failed. Review the stage details and error message for remediation.`, }); } const combinedInsights = runDetail.insights && runDetail.insights.length > 0 ? [...runDetail.insights, ...derivedInsights] : derivedInsights; return ( <>
{/* Run Summary */} {normalizedRun && } {/* Insights Panel */} {combinedInsights.length > 0 && ( )} {/* Two Column Layout */}
{/* Left Column - Credit Breakdown & Efficiency */}
{runDetail.stages && } {runDetail.efficiency && runDetail.historical_comparison && ( )}
{/* Right Column - Stage Details */}
{runDetail.stages && runDetail.run?.initial_snapshot && ( )}
); }; export default AutomationRunDetail;