/** * Tasks Panel Component * Displays tasks with their status */ import React, { useEffect } from 'react'; import { useContentPlan } from '../context/ContentPlanContext'; import { Task } from '../types'; interface TasksPanelProps { taskId?: string | null; onClose?: () => void; } const TasksPanel: React.FC = ({ taskId, onClose }) => { const { tasks, tasksLoading, tasksError, fetchTasks, getString, setCurrentTaskId, markTaskAsWatched, setTaskFilter, taskFilterId, contentPlan, } = useContentPlan(); useEffect(() => { // Fetch tasks when taskId changes (initial fetch is handled by ContentPlanContext on mount) // Note: Polling is now handled in ContentPlanContext if (taskId) { fetchTasks({ limit: 20 }); } }, [taskId]); // Removed fetchTasks from dependencies as it's stable (useCallback) const getStatusBadgeClasses = (status: Task['status']): string => { switch (status) { case 'completed': return 'text-green-600 bg-green-50'; case 'failed': return 'text-red-600 bg-red-50'; case 'processing': return 'text-blue-600 bg-blue-50'; case 'queued': return 'text-yellow-600 bg-yellow-50'; default: return 'text-gray-600 bg-gray-50'; } }; const getTaskContainerClasses = (status: Task['status']): string => { switch (status) { case 'completed': return 'bg-green-50 border-green-200'; case 'failed': return 'bg-red-50 border-red-200'; case 'processing': return 'bg-blue-50 border-blue-200'; case 'queued': return 'bg-yellow-50 border-yellow-200'; default: return 'bg-gray-50 border-gray-200'; } }; const formatTypeForDisplay = (value: string): string => value .split(/[_\s-]+/) .filter(Boolean) .map( word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase() ) .join(' '); const getTaskTitle = (type: Task['type']): string => { const normalizedType = type?.toLowerCase(); switch (normalizedType) { case 'content_plan_generation': case 'content_plan': return getString( 'tasks_panel', 'content_plan_generation', 'Content plan generation task' ); case 'post_generation': case 'post': return getString( 'tasks_panel', 'post_generation', 'Post generation task' ); default: return type ? `${formatTypeForDisplay(type)} task` : getString('tasks_panel', 'generic_task', 'Task'); } }; const getStatusLabel = (status: Task['status']): string => { switch (status) { case 'completed': return 'Completed'; case 'failed': return 'Failed'; case 'processing': return 'Processing'; case 'queued': return 'Queued'; default: return status; } }; return ( <> {tasksLoading && tasks.length === 0 && (

{getString( 'generation_modal', 'loading_tasks', 'Loading tasks...' )}

)} {tasksError && (

{getString( 'generation_modal', 'error_loading_tasks', 'Error loading tasks:' )}{' '} {tasksError}

)} {!tasksLoading && tasks.length === 0 && (

{getString( 'generation_modal', 'no_tasks', 'No tasks found' )}

)} {tasks.length > 0 && (
{tasks .filter(task => !task.watched) // Filter out watched tasks .map(task => (

{getTaskTitle(task.type)}

Status:{' '} {task.status === 'processing' ? ( {getStatusLabel( task.status )} ) : ( getStatusLabel( task.status ) )}

Created:{' '} {new Date( task.created_at ).toLocaleString()}

{task.status === 'completed' && (() => { // Check for content plan generation tasks (have item_ids) const itemIds = task.result?.output?.item_ids; const hasItemIds = itemIds && Array.isArray(itemIds) && itemIds.length > 0; // Check for post generation tasks (have item_id in input) const itemId = task.result?.input?.item_id; const hasItemId = itemId !== undefined && itemId !== null; // For post generation tasks, get the formatted scheduled date from task const formattedScheduledDate = task.formatted_scheduled_date || null; // Show filter link if task has item_ids (content plan generation) or item_id (post generation) if (hasItemIds || hasItemId) { const filterText = hasItemIds ? getString( 'tasks_panel', 'filter_items', `Show ${itemIds.length} item${itemIds.length === 1 ? '' : 's'}` ) : getString( 'tasks_panel', 'filter_post', 'Show generated post' ); return (
{formattedScheduledDate && (

{getString( 'tasks_panel', 'auto_publish', 'Will auto-publish on:' )} {' '} { formattedScheduledDate }

)}
); } return null; })()} {task.status === 'failed' && (
{task.result && typeof task.result === 'object' && task.result.error ? ( <> {/* Check moderation flags from task.moderation or task.result */} {(task as any).moderation ?.blocked || task.result .moderation_blocked ? (

ACCOUNT BLOCKED

{task.result.error?.replace( /\\n/g, '\n' )}
) : (task as any).moderation ?.warning || task.result .moderation_warning ? (
{task.result.error?.replace( /\\n/g, '\n' )}

You can modify your request and try again.

) : (

Task Failed

{task.result.error?.replace( /\\n/g, '\n' )}
{(task as any) .moderation ?.can_retry !== false && task.result .can_retry !== false && (

You can retry this task.

)}
)} ) : (

{getString( 'tasks_panel', 'loading_error_details', 'Loading error details...' )}

)}
)}
))}
)} ); }; export default TasksPanel;