alot of othe mess fro autoamtion overview an ddetiaeld run apge sonly
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
* Automation Run Detail Page
|
||||
* Comprehensive view of a single automation run
|
||||
*/
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { useSiteStore } from '../../store/siteStore';
|
||||
import { automationService } from '../../services/automationService';
|
||||
@@ -20,29 +20,74 @@ const AutomationRunDetail: React.FC = () => {
|
||||
const { runId } = useParams<{ runId: string }>();
|
||||
const navigate = useNavigate();
|
||||
const { activeSite } = useSiteStore();
|
||||
const toast = useToast();
|
||||
const { error: toastError } = useToast();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [runDetail, setRunDetail] = useState<RunDetailResponse | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const lastRequestKey = useRef<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
loadRunDetail();
|
||||
}, [runId, activeSite]);
|
||||
|
||||
const loadRunDetail = async () => {
|
||||
if (!activeSite || !runId) return;
|
||||
|
||||
const decodeTitle = (value: string | undefined | null) => {
|
||||
if (!value) return '';
|
||||
try {
|
||||
setLoading(true);
|
||||
const data = await automationService.getRunDetail(activeSite.id, runId);
|
||||
setRunDetail(data);
|
||||
} catch (error: any) {
|
||||
console.error('Failed to load run detail', error);
|
||||
toast.error(error.message || 'Failed to load run detail');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
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 (
|
||||
<div className="p-6">
|
||||
@@ -59,6 +104,14 @@ const AutomationRunDetail: React.FC = () => {
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<p className="text-gray-600 dark:text-gray-400">{error}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!runDetail) {
|
||||
return (
|
||||
<div className="p-6">
|
||||
@@ -67,26 +120,71 @@ const AutomationRunDetail: React.FC = () => {
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<>
|
||||
<PageMeta
|
||||
title={`Run Detail - ${runDetail.run?.run_title || 'Automation Run'}`}
|
||||
title={`Run Detail - ${displayTitle}`}
|
||||
description="Detailed automation run analysis"
|
||||
/>
|
||||
|
||||
<div className="space-y-6">
|
||||
<PageHeader
|
||||
title={runDetail.run?.run_title || 'Automation Run'}
|
||||
breadcrumb={`Automation / Runs / ${runDetail.run?.run_title || 'Detail'}`}
|
||||
title={displayTitle}
|
||||
breadcrumb={`Automation / Runs / ${breadcrumbLabel || 'Detail'}`}
|
||||
description="Comprehensive run analysis with stage breakdown and performance metrics"
|
||||
/>
|
||||
|
||||
{/* Run Summary */}
|
||||
{runDetail.run && <RunSummaryCard run={runDetail.run} />}
|
||||
{normalizedRun && <RunSummaryCard run={normalizedRun} summary={stageSummary} />}
|
||||
|
||||
{/* Insights Panel */}
|
||||
{runDetail.insights && runDetail.insights.length > 0 && (
|
||||
<InsightsPanel insights={runDetail.insights} />
|
||||
{combinedInsights.length > 0 && (
|
||||
<InsightsPanel insights={combinedInsights} />
|
||||
)}
|
||||
|
||||
{/* Two Column Layout */}
|
||||
|
||||
Reference in New Issue
Block a user