import React, { useState, useEffect } from 'react'; import { Paper, Typography, Box, List, ListItem, Chip, Button, LinearProgress, Grid, Divider, Alert, IconButton, Tooltip } from '@mui/material'; import { CheckCircleRounded, ErrorRounded, HourglassEmptyRounded, PlayArrowRounded, CancelRounded, DownloadRounded, RefreshRounded } from '@mui/icons-material'; import { JOB_STATUS, API_BASE_URL } from '../utils/constants'; import { getUserJobs, getQueueStatus, cancelJob } from '../services/api'; const QueueManager = ({ userEmail }) => { const [jobs, setJobs] = useState([]); const [queueStatus, setQueueStatus] = useState({}); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); const fetchUserJobs = async () => { try { console.log('Fetching user jobs for:', userEmail); const data = await getUserJobs(userEmail); console.log('User jobs data:', data); setJobs(data.jobs || []); } catch (err) { console.error('fetchUserJobs error:', err); setError(`Failed to load jobs: ${err.message}`); } }; const fetchQueueStatusData = async () => { try { console.log('Fetching queue status'); const data = await getQueueStatus(); console.log('Queue status data:', data); setQueueStatus(data); } catch (err) { console.error('fetchQueueStatus error:', err); } }; const handleCancelJob = async (jobId) => { try { await cancelJob(jobId); await fetchUserJobs(); // Refresh the list } catch (err) { setError(`Failed to cancel job: ${err.message}`); } }; useEffect(() => { const loadData = async () => { setLoading(true); await Promise.all([fetchUserJobs(), fetchQueueStatusData()]); setLoading(false); }; loadData(); // Poll for updates every 2 seconds const interval = setInterval(() => { fetchUserJobs(); fetchQueueStatusData(); }, 2000); return () => clearInterval(interval); }, [userEmail]); const getStatusIcon = (status) => { if (status === JOB_STATUS.COMPLETED) return ; if (status === JOB_STATUS.FAILED) return ; if (status === JOB_STATUS.CANCELLED) return ; if (status === JOB_STATUS.QUEUED) return ; if (status.startsWith('retry_')) return ; return ; }; const getStatusColor = (status) => { if (status === JOB_STATUS.COMPLETED) return 'success'; if (status === JOB_STATUS.FAILED) return 'error'; if (status === JOB_STATUS.CANCELLED) return 'default'; if (status === JOB_STATUS.QUEUED) return 'info'; if (status.startsWith('retry_')) return 'warning'; return 'primary'; }; const getProgressValue = (job) => { if (job.status === JOB_STATUS.COMPLETED) return 100; if (job.status === JOB_STATUS.FAILED || job.status === JOB_STATUS.CANCELLED) return 0; return job.progress || 0; }; const renderDownloadButtons = (job) => { if (job.status !== JOB_STATUS.COMPLETED) return null; const videoCount = job.video_count || 1; const buttons = []; // Main download button (zip for multiple videos, single video for one) buttons.push( ); // Individual download buttons for multiple videos if (videoCount > 1) { for (let i = 1; i <= videoCount; i++) { buttons.push( ); } } return {buttons}; }; if (loading) { return ( Job Queue ); } return ( Job Queue { fetchUserJobs(); fetchQueueStatusData(); }}> {/* Queue Status Summary */} {queueStatus.queue_length !== undefined && ( Queue: {queueStatus.queue_length} waiting • Processing: {queueStatus.processing_jobs}/{queueStatus.concurrent_limit} • Your jobs: {jobs.length}/4 )} {error && ( setError('')}> {error} )} {jobs.length === 0 ? ( No jobs found. Submit a video generation request to see it here. ) : ( {jobs.map((job, index) => ( {getStatusIcon(job.status)} {job.videos_requested} video{job.videos_requested > 1 ? 's' : ''} {job.status === JOB_STATUS.QUEUED && job.queue_position > 0 && ( )} {job.can_cancel && job.status === JOB_STATUS.QUEUED && ( )} {job.prompt.length > 100 ? `${job.prompt.substring(0, 100)}...` : job.prompt} {job.message || 'Processing...'} {/* Progress Bar */} {/* Download Buttons */} {renderDownloadButtons(job)} {/* Job Details */} Created: {new Date(job.created_at).toLocaleString()} {job.started_at && ( Started: {new Date(job.started_at).toLocaleString()} )} Model: {job.model_name?.includes('fast') ? 'Fast' : 'Standard'} Length: {job.video_length_sec}s {/* Retry Information */} {job.retry_count > 0 && ( Retry attempt {job.retry_count} of {job.max_retries}
{job.status.includes('retry') && job.message && ( {job.message} )} {job.error && job.error.includes('timeout') && (
Note: This is likely due to network connectivity issues. The system will automatically retry with longer timeouts.
)}
)} {/* Final Failure Information */} {job.final_failure && ( Failed after {job.retry_count} retries. {job.error} )}
{index < jobs.length - 1 && }
))}
)}
); }; export default QueueManager;