autoamtiona nd other pages udpates,
This commit is contained in:
@@ -191,7 +191,7 @@ const CurrentProcessingCard: React.FC<CurrentProcessingCardProps> = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`rounded-xl p-5 mb-6 border-2 transition-all ${
|
||||
<div className={`rounded-2xl p-5 mb-6 border-2 transition-all ${
|
||||
isPaused
|
||||
? 'bg-gradient-to-r from-warning-50 to-warning-100 dark:from-warning-900/20 dark:to-warning-800/20 border-warning-400'
|
||||
: 'bg-gradient-to-r from-brand-50 to-brand-100 dark:from-brand-900/20 dark:to-brand-800/20 border-brand-400'
|
||||
@@ -199,24 +199,26 @@ const CurrentProcessingCard: React.FC<CurrentProcessingCardProps> = ({
|
||||
{/* Header Row */}
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={`size-10 rounded-lg flex items-center justify-center shadow-md ${
|
||||
<div className={`size-12 rounded-xl flex items-center justify-center shadow-lg ${
|
||||
isPaused
|
||||
? 'bg-gradient-to-br from-warning-500 to-warning-600'
|
||||
: 'bg-gradient-to-br from-brand-500 to-brand-600'
|
||||
}`}>
|
||||
{isPaused ? (
|
||||
<PauseIcon className="w-5 h-5 text-white" />
|
||||
<PauseIcon className="w-6 h-6 text-white" />
|
||||
) : (
|
||||
<BoltIcon className="w-5 h-5 text-white animate-pulse" />
|
||||
<BoltIcon className="w-6 h-6 text-white animate-pulse" />
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-lg font-bold text-gray-900 dark:text-white">
|
||||
<h2 className="text-xl font-bold text-gray-900 dark:text-white">
|
||||
{isPaused ? 'Automation Paused' : 'Automation In Progress'}
|
||||
</h2>
|
||||
<div className="text-sm text-gray-600 dark:text-gray-400">
|
||||
Stage {currentRun.current_stage}: {stageName}
|
||||
<span className={`ml-2 px-1.5 py-0.5 rounded text-xs ${
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<span className={`font-semibold ${isPaused ? 'text-warning-700' : 'text-brand-700'}`}>
|
||||
Stage {currentRun.current_stage}: {stageName}
|
||||
</span>
|
||||
<span className={`px-2 py-0.5 rounded-full text-xs font-medium ${
|
||||
isPaused ? 'bg-warning-200 text-warning-800' : 'bg-brand-200 text-brand-800'
|
||||
}`}>
|
||||
{stageOverview?.type || 'AI'}
|
||||
@@ -225,13 +227,14 @@ const CurrentProcessingCard: React.FC<CurrentProcessingCardProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Close and Actions */}
|
||||
<div className="flex items-center gap-2">
|
||||
<Button variant="ghost" size="sm" onClick={onClose}>
|
||||
<XMarkIcon className="w-4 h-4" />
|
||||
<span className="ml-1">Close</span>
|
||||
</Button>
|
||||
</div>
|
||||
{/* Close Button - Top Right */}
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors p-1"
|
||||
title="Close"
|
||||
>
|
||||
<XMarkIcon className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Progress Section */}
|
||||
@@ -241,37 +244,38 @@ const CurrentProcessingCard: React.FC<CurrentProcessingCardProps> = ({
|
||||
{/* Progress Text */}
|
||||
<div className="flex items-baseline justify-between mb-2">
|
||||
<div className="flex items-baseline gap-3">
|
||||
<span className={`text-4xl font-bold ${isPaused ? 'text-warning-600' : 'text-brand-600'}`}>
|
||||
<span className={`text-5xl font-bold ${isPaused ? 'text-warning-600' : 'text-brand-600'}`}>
|
||||
{displayPercent}%
|
||||
</span>
|
||||
<span className="text-sm text-gray-500">
|
||||
<span className="text-base font-medium text-gray-600">
|
||||
{realProcessed}/{realTotal} {outputLabel}
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-xs text-gray-400">
|
||||
<span className="text-sm font-medium text-gray-500">
|
||||
{realTotal - realProcessed} remaining
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Progress Bar */}
|
||||
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-3 overflow-hidden">
|
||||
{/* Progress Bar - Vibrant */}
|
||||
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-4 overflow-hidden shadow-inner">
|
||||
<div
|
||||
className={`h-3 rounded-full transition-all duration-300 ${
|
||||
className={`h-4 rounded-full transition-all duration-300 shadow-sm ${
|
||||
isPaused
|
||||
? 'bg-gradient-to-r from-warning-400 to-warning-600'
|
||||
: 'bg-gradient-to-r from-brand-400 to-brand-600'
|
||||
? 'bg-gradient-to-r from-warning-400 via-warning-500 to-warning-600'
|
||||
: 'bg-gradient-to-r from-brand-400 via-brand-500 to-brand-600'
|
||||
} ${!isPaused && displayPercent < 100 ? 'animate-pulse' : ''}`}
|
||||
style={{ width: `${Math.min(displayPercent, 100)}%` }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Control Buttons */}
|
||||
{/* Control Buttons - Clean Design */}
|
||||
<div className="flex items-center gap-3 mt-4">
|
||||
{currentRun.status === 'running' ? (
|
||||
<Button
|
||||
onClick={handlePause}
|
||||
disabled={isPausing}
|
||||
variant="secondary"
|
||||
variant="outline"
|
||||
tone="warning"
|
||||
size="sm"
|
||||
startIcon={<PauseIcon className="w-4 h-4" />}
|
||||
>
|
||||
@@ -282,6 +286,7 @@ const CurrentProcessingCard: React.FC<CurrentProcessingCardProps> = ({
|
||||
onClick={handleResume}
|
||||
disabled={isResuming}
|
||||
variant="primary"
|
||||
tone="success"
|
||||
size="sm"
|
||||
startIcon={<PlayIcon className="w-4 h-4" />}
|
||||
>
|
||||
@@ -289,43 +294,44 @@ const CurrentProcessingCard: React.FC<CurrentProcessingCardProps> = ({
|
||||
</Button>
|
||||
) : null}
|
||||
|
||||
<button
|
||||
<Button
|
||||
onClick={handleCancel}
|
||||
disabled={isCancelling}
|
||||
className="text-sm text-gray-500 hover:text-error-600 transition-colors"
|
||||
variant="ghost"
|
||||
tone="neutral"
|
||||
size="sm"
|
||||
>
|
||||
<XMarkIcon className="w-4 h-4 inline mr-1" />
|
||||
{isCancelling ? 'Cancelling...' : 'Cancel'}
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Metrics - 30% width */}
|
||||
<div className="w-44 space-y-2">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg px-3 py-2 border border-gray-200 dark:border-gray-700 flex items-center justify-between">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl px-3 py-2.5 border border-gray-200 dark:border-gray-700 flex items-center justify-between">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<ClockIcon className="w-4 h-4 text-gray-400" />
|
||||
<span className="text-xs text-gray-500 uppercase">Duration</span>
|
||||
<span className="text-xs font-medium text-gray-500 uppercase">Duration</span>
|
||||
</div>
|
||||
<span className="text-sm font-bold text-gray-900 dark:text-white">{formatDuration(currentRun.started_at)}</span>
|
||||
<span className="text-base font-bold text-gray-900 dark:text-white">{formatDuration(currentRun.started_at)}</span>
|
||||
</div>
|
||||
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg px-3 py-2 border border-gray-200 dark:border-gray-700 flex items-center justify-between">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl px-3 py-2.5 border border-gray-200 dark:border-gray-700 flex items-center justify-between">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<BoltIcon className="w-4 h-4 text-warning-500" />
|
||||
<span className="text-xs text-gray-500 uppercase">Credits</span>
|
||||
<span className="text-xs font-medium text-gray-500 uppercase">Credits</span>
|
||||
</div>
|
||||
<span className="text-sm font-bold text-warning-600">{currentRun.total_credits_used}</span>
|
||||
<span className="text-base font-bold text-warning-600">{currentRun.total_credits_used}</span>
|
||||
</div>
|
||||
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg px-3 py-2 border border-gray-200 dark:border-gray-700 flex items-center justify-between">
|
||||
<span className="text-xs text-gray-500 uppercase">Stage</span>
|
||||
<span className="text-sm font-bold text-gray-900 dark:text-white">{currentRun.current_stage} of 7</span>
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl px-3 py-2.5 border border-gray-200 dark:border-gray-700 flex items-center justify-between">
|
||||
<span className="text-xs font-medium text-gray-500 uppercase">Stage</span>
|
||||
<span className="text-base font-bold text-gray-900 dark:text-white">{currentRun.current_stage} of 7</span>
|
||||
</div>
|
||||
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg px-3 py-2 border border-gray-200 dark:border-gray-700 flex items-center justify-between">
|
||||
<span className="text-xs text-gray-500 uppercase">Status</span>
|
||||
<span className={`text-sm font-bold ${isPaused ? 'text-warning-600' : 'text-success-600'}`}>
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl px-3 py-2.5 border border-gray-200 dark:border-gray-700 flex items-center justify-between">
|
||||
<span className="text-xs font-medium text-gray-500 uppercase">Status</span>
|
||||
<span className={`text-base font-bold ${isPaused ? 'text-warning-600' : 'text-success-600'}`}>
|
||||
{isPaused ? 'Paused' : 'Running'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -7,15 +7,15 @@ import React from 'react';
|
||||
import { AutomationRun, InitialSnapshot, StageProgress, GlobalProgress } from '../../services/automationService';
|
||||
import { BoltIcon, CheckCircleIcon, PauseIcon } from '../../icons';
|
||||
|
||||
// Stage colors matching AutomationPage STAGE_CONFIG
|
||||
// Stage colors matching AutomationPage STAGE_CONFIG exactly
|
||||
const STAGE_COLORS = [
|
||||
'from-brand-500 to-brand-600', // Stage 1: Keywords → Clusters
|
||||
'from-purple-500 to-purple-600', // Stage 2: Clusters → Ideas
|
||||
'from-purple-500 to-purple-600', // Stage 3: Ideas → Tasks
|
||||
'from-success-500 to-success-600', // Stage 4: Tasks → Content
|
||||
'from-warning-500 to-warning-600', // Stage 5: Content → Image Prompts
|
||||
'from-purple-500 to-purple-600', // Stage 6: Image Prompts → Images
|
||||
'from-success-500 to-success-600', // Stage 7: Manual Review Gate
|
||||
'from-brand-500 to-brand-600', // Stage 1: Keywords → Clusters (brand/teal)
|
||||
'from-purple-500 to-purple-600', // Stage 2: Clusters → Ideas (purple)
|
||||
'from-warning-500 to-warning-600', // Stage 3: Ideas → Tasks (amber)
|
||||
'from-brand-500 to-brand-600', // Stage 4: Tasks → Content (brand/teal)
|
||||
'from-success-500 to-success-600', // Stage 5: Content → Image Prompts (green)
|
||||
'from-purple-500 to-purple-600', // Stage 6: Image Prompts → Images (purple)
|
||||
'from-success-500 to-success-600', // Stage 7: Review Gate (green)
|
||||
];
|
||||
|
||||
const STAGE_NAMES = [
|
||||
@@ -25,7 +25,7 @@ const STAGE_NAMES = [
|
||||
'Tasks',
|
||||
'Content',
|
||||
'Prompts',
|
||||
'Review',
|
||||
'Publish',
|
||||
];
|
||||
|
||||
// Helper to get processed count from stage result using correct key
|
||||
@@ -76,13 +76,9 @@ const GlobalProgressBar: React.FC<GlobalProgressBarProps> = ({
|
||||
|
||||
// Fallback: Calculate from currentRun and initialSnapshot
|
||||
const snapshot = initialSnapshot || (currentRun as any)?.initial_snapshot;
|
||||
if (!snapshot) {
|
||||
return { percentage: 0, completed: 0, total: 0 };
|
||||
}
|
||||
|
||||
const totalInitial = snapshot.total_initial_items || 0;
|
||||
// Calculate total completed from all stage results
|
||||
let totalCompleted = 0;
|
||||
|
||||
for (let i = 1; i <= 7; i++) {
|
||||
const result = (currentRun as any)[`stage_${i}_result`];
|
||||
if (result) {
|
||||
@@ -90,12 +86,26 @@ const GlobalProgressBar: React.FC<GlobalProgressBarProps> = ({
|
||||
}
|
||||
}
|
||||
|
||||
const percentage = totalInitial > 0 ? Math.round((totalCompleted / totalInitial) * 100) : 0;
|
||||
// Calculate total items - sum of ALL stage initials from snapshot (updated after each stage)
|
||||
// This accounts for items created during the run (e.g., keywords create clusters, clusters create ideas)
|
||||
let totalItems = 0;
|
||||
if (snapshot) {
|
||||
for (let i = 1; i <= 7; i++) {
|
||||
const stageInitial = snapshot[`stage_${i}_initial`] || 0;
|
||||
totalItems += stageInitial;
|
||||
}
|
||||
}
|
||||
|
||||
// Use the updated total from snapshot, or fallback to total_initial_items
|
||||
const finalTotal = totalItems > 0 ? totalItems : (snapshot?.total_initial_items || 0);
|
||||
|
||||
// Ensure completed never exceeds total (clamp percentage to 100%)
|
||||
const percentage = finalTotal > 0 ? Math.round((totalCompleted / finalTotal) * 100) : 0;
|
||||
|
||||
return {
|
||||
percentage: Math.min(percentage, 100),
|
||||
completed: totalCompleted,
|
||||
total: totalInitial,
|
||||
total: finalTotal,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -139,46 +149,46 @@ const GlobalProgressBar: React.FC<GlobalProgressBarProps> = ({
|
||||
|
||||
return (
|
||||
<div className={`
|
||||
rounded-xl p-4 mb-6 border-2 transition-all
|
||||
rounded-2xl p-5 mb-6 border-2 transition-all
|
||||
${isPaused
|
||||
? 'bg-gradient-to-r from-warning-50 to-warning-100 dark:from-warning-900/20 dark:to-warning-800/20 border-warning-300 dark:border-warning-700'
|
||||
: 'bg-gradient-to-r from-brand-50 to-brand-100 dark:from-brand-900/20 dark:to-brand-800/20 border-brand-300 dark:border-brand-700'
|
||||
}
|
||||
`}>
|
||||
{/* Header Row */}
|
||||
<div className="flex justify-between items-center mb-3">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={`
|
||||
size-10 rounded-lg flex items-center justify-center shadow-md
|
||||
size-12 rounded-xl flex items-center justify-center shadow-lg
|
||||
${isPaused
|
||||
? 'bg-gradient-to-br from-warning-500 to-warning-600'
|
||||
: 'bg-gradient-to-br from-brand-500 to-brand-600'
|
||||
}
|
||||
`}>
|
||||
{isPaused ? (
|
||||
<PauseIcon className="w-5 h-5 text-white" />
|
||||
<PauseIcon className="w-6 h-6 text-white" />
|
||||
) : percentage >= 100 ? (
|
||||
<CheckCircleIcon className="w-5 h-5 text-white" />
|
||||
<CheckCircleIcon className="w-6 h-6 text-white" />
|
||||
) : (
|
||||
<BoltIcon className="w-5 h-5 text-white animate-pulse" />
|
||||
<BoltIcon className="w-6 h-6 text-white animate-pulse" />
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<div className={`font-bold ${isPaused ? 'text-warning-800 dark:text-warning-200' : 'text-brand-800 dark:text-brand-200'}`}>
|
||||
<div className={`text-lg font-bold ${isPaused ? 'text-warning-800 dark:text-warning-200' : 'text-brand-800 dark:text-brand-200'}`}>
|
||||
{isPaused ? 'Pipeline Paused' : 'Full Pipeline Progress'}
|
||||
</div>
|
||||
<div className="text-xs text-gray-600 dark:text-gray-400">
|
||||
<div className="text-sm text-gray-600 dark:text-gray-400">
|
||||
Stage {currentStage} of 7 • {formatDuration()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={`text-3xl font-bold ${isPaused ? 'text-warning-600 dark:text-warning-400' : 'text-brand-600 dark:text-brand-400'}`}>
|
||||
<div className={`text-4xl font-bold ${isPaused ? 'text-warning-600 dark:text-warning-400' : 'text-brand-600 dark:text-brand-400'}`}>
|
||||
{percentage}%
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Segmented Progress Bar */}
|
||||
<div className="flex h-4 rounded-full overflow-hidden bg-gray-200 dark:bg-gray-700 gap-0.5 mb-2">
|
||||
{/* Segmented Progress Bar - Taller & More Vibrant */}
|
||||
<div className="flex h-5 rounded-full overflow-hidden bg-gray-200 dark:bg-gray-700 gap-0.5 mb-3 shadow-inner">
|
||||
{[1, 2, 3, 4, 5, 6, 7].map(stageNum => {
|
||||
const status = getStageStatus(stageNum);
|
||||
const stageColor = STAGE_COLORS[stageNum - 1];
|
||||
@@ -188,9 +198,9 @@ const GlobalProgressBar: React.FC<GlobalProgressBarProps> = ({
|
||||
key={stageNum}
|
||||
className={`flex-1 transition-all duration-500 relative group ${
|
||||
status === 'completed'
|
||||
? `bg-gradient-to-r ${stageColor}`
|
||||
? `bg-gradient-to-r ${stageColor} shadow-sm`
|
||||
: status === 'active'
|
||||
? `bg-gradient-to-r ${stageColor} opacity-60 ${!isPaused ? 'animate-pulse' : ''}`
|
||||
? `bg-gradient-to-r ${stageColor} opacity-70 ${!isPaused ? 'animate-pulse' : ''} shadow-sm`
|
||||
: 'bg-gray-300 dark:bg-gray-600'
|
||||
}`}
|
||||
title={`Stage ${stageNum}: ${STAGE_NAMES[stageNum - 1]}`}
|
||||
@@ -206,20 +216,22 @@ const GlobalProgressBar: React.FC<GlobalProgressBarProps> = ({
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Footer Row */}
|
||||
<div className="flex justify-between text-xs text-gray-600 dark:text-gray-400">
|
||||
<span>{completed} / {total} items processed</span>
|
||||
<div className="flex gap-4">
|
||||
{/* Footer Row - Larger Font for Stage Numbers */}
|
||||
<div className="flex justify-between items-center text-sm text-gray-700 dark:text-gray-300">
|
||||
<span className="font-medium">{completed} / {total} items processed</span>
|
||||
<div className="flex gap-3">
|
||||
{[1, 2, 3, 4, 5, 6, 7].map(stageNum => {
|
||||
const status = getStageStatus(stageNum);
|
||||
return (
|
||||
<span
|
||||
key={stageNum}
|
||||
className={`
|
||||
${status === 'completed' ? 'text-success-600 dark:text-success-400 font-medium' : ''}
|
||||
${status === 'active' ? `${isPaused ? 'text-warning-600 dark:text-warning-400' : 'text-brand-600 dark:text-brand-400'} font-bold` : ''}
|
||||
${status === 'pending' ? 'text-gray-400 dark:text-gray-500' : ''}
|
||||
`}
|
||||
className={`text-base font-semibold ${
|
||||
status === 'completed' ? 'text-success-600 dark:text-success-400' : ''
|
||||
} ${
|
||||
status === 'active' ? `${isPaused ? 'text-warning-600 dark:text-warning-400' : 'text-brand-600 dark:text-brand-400'}` : ''
|
||||
} ${
|
||||
status === 'pending' ? 'text-gray-400 dark:text-gray-500' : ''
|
||||
}`}
|
||||
>
|
||||
{stageNum}
|
||||
{status === 'completed' && '✓'}
|
||||
|
||||
@@ -116,11 +116,8 @@ export default function PageHeader({
|
||||
|
||||
return (
|
||||
<div className={`${className}`}>
|
||||
{/* Title now shown in AppHeader - this component only triggers the context update */}
|
||||
{/* Show description if provided - can be used for additional context */}
|
||||
{description && (
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400 mb-2">{description}</p>
|
||||
)}
|
||||
{/* Title and description now shown in AppHeader - this component only triggers the context update */}
|
||||
{/* Description rendering removed - visible in AppHeader tooltip or not needed */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user