import React, { useState, useEffect } from 'react' import { Progress } from '@/components/ui/progress' import { Button } from '@/components/ui/button' import { Card, CardContent } from '@/components/ui/card' import { Loader2, CheckCircle, XCircle, AlertCircle, Clock, X, Pause, Play, Square } from 'lucide-react' export interface ProgressOperation { id: string title: string description?: string total: number completed: number status: 'running' | 'paused' | 'completed' | 'error' | 'cancelled' error?: string startTime: number estimatedTimeRemaining?: number canCancel?: boolean canPause?: boolean onCancel?: () => void onPause?: () => void onResume?: () => void } interface ProgressManagerContextType { operations: ProgressOperation[] startOperation: (operation: Omit) => string updateOperation: (id: string, updates: Partial) => void completeOperation: (id: string, success?: boolean, error?: string) => void cancelOperation: (id: string) => void pauseOperation: (id: string) => void resumeOperation: (id: string) => void removeOperation: (id: string) => void } const ProgressManagerContext = React.createContext(undefined) export function useProgress() { const context = React.useContext(ProgressManagerContext) if (!context) { throw new Error('useProgress must be used within a ProgressProvider') } return context } interface ProgressProviderProps { children: React.ReactNode maxOperations?: number } export function ProgressProvider({ children, maxOperations = 5 }: ProgressProviderProps) { const [operations, setOperations] = useState([]) const startOperation = (operation: Omit) => { const id = Math.random().toString(36).substring(2, 15) const newOperation: ProgressOperation = { ...operation, id, startTime: Date.now(), status: 'running', completed: 0 } setOperations(prev => { const updated = [newOperation, ...prev] return updated.slice(0, maxOperations) }) return id } const updateOperation = (id: string, updates: Partial) => { setOperations(prev => prev.map(op => { if (op.id === id) { const updated = { ...op, ...updates } // Calculate estimated time remaining if (updated.completed > 0 && updated.total > 0 && updated.status === 'running') { const elapsed = Date.now() - op.startTime const rate = updated.completed / elapsed const remaining = (updated.total - updated.completed) / rate updated.estimatedTimeRemaining = remaining } return updated } return op })) } const completeOperation = (id: string, success = true, error?: string) => { updateOperation(id, { status: success ? 'completed' : 'error', completed: success ? operations.find(op => op.id === id)?.total || 0 : undefined, error: error }) // Auto-remove completed operations after 5 seconds setTimeout(() => { removeOperation(id) }, 5000) } const cancelOperation = (id: string) => { const operation = operations.find(op => op.id === id) if (operation?.onCancel) { operation.onCancel() } updateOperation(id, { status: 'cancelled' }) // Auto-remove cancelled operations after 3 seconds setTimeout(() => { removeOperation(id) }, 3000) } const pauseOperation = (id: string) => { const operation = operations.find(op => op.id === id) if (operation?.onPause) { operation.onPause() } updateOperation(id, { status: 'paused' }) } const resumeOperation = (id: string) => { const operation = operations.find(op => op.id === id) if (operation?.onResume) { operation.onResume() } updateOperation(id, { status: 'running' }) } const removeOperation = (id: string) => { setOperations(prev => prev.filter(op => op.id !== id)) } const value: ProgressManagerContextType = { operations, startOperation, updateOperation, completeOperation, cancelOperation, pauseOperation, resumeOperation, removeOperation } return ( {children} ) } function ProgressOverlay() { const { operations } = useProgress() const activeOperations = operations.filter(op => op.status === 'running' || op.status === 'paused' || (op.status === 'completed' || op.status === 'error' || op.status === 'cancelled') ) if (activeOperations.length === 0) { return null } return (
{activeOperations.map((operation) => ( ))}
) } interface ProgressCardProps { operation: ProgressOperation } function ProgressCard({ operation }: ProgressCardProps) { const { cancelOperation, pauseOperation, resumeOperation, removeOperation } = useProgress() const [isVisible, setIsVisible] = useState(false) useEffect(() => { const timer = setTimeout(() => setIsVisible(true), 50) return () => clearTimeout(timer) }, []) const handleDismiss = () => { setIsVisible(false) setTimeout(() => removeOperation(operation.id), 300) } const getProgressPercentage = () => { if (operation.total === 0) return 0 return Math.round((operation.completed / operation.total) * 100) } const getStatusIcon = () => { switch (operation.status) { case 'running': return case 'paused': return case 'completed': return case 'error': return case 'cancelled': return default: return } } const getStatusColor = () => { switch (operation.status) { case 'running': return 'border-blue-500/30' case 'paused': return 'border-yellow-500/30' case 'completed': return 'border-green-500/30' case 'error': return 'border-red-500/30' case 'cancelled': return 'border-gray-500/30' default: return 'border-gray-600/30' } } const formatTimeRemaining = (ms: number) => { const seconds = Math.round(ms / 1000) if (seconds < 60) return `${seconds}s` const minutes = Math.round(seconds / 60) if (minutes < 60) return `${minutes}m` const hours = Math.round(minutes / 60) return `${hours}h` } return (
{getStatusIcon()}
{/* Header */}

{operation.title}

{/* Description */} {operation.description && (

{operation.description}

)} {/* Progress Bar */}
{operation.completed} of {operation.total} {operation.status === 'running' && operation.estimatedTimeRemaining && ( ({formatTimeRemaining(operation.estimatedTimeRemaining)} remaining) )} {getProgressPercentage()}%
{/* Error Message */} {operation.status === 'error' && operation.error && (
Error:
{operation.error}
)} {/* Status Message */}
{operation.status === 'running' && 'In progress...'} {operation.status === 'paused' && 'Paused'} {operation.status === 'completed' && 'Completed successfully'} {operation.status === 'error' && 'Failed'} {operation.status === 'cancelled' && 'Cancelled'}
{/* Action Buttons */} {(operation.status === 'running' || operation.status === 'paused') && (
{operation.status === 'running' && operation.canPause && ( )} {operation.status === 'paused' && ( )} {operation.canCancel && ( )}
)}
) } // Hook for common progress operations export function useOperationProgress() { const progress = useProgress() const startBulkOperation = (title: string, items: any[], description?: string) => { return progress.startOperation({ title, description: description || `Processing ${items.length} items`, total: items.length, completed: 0, canCancel: true, canPause: false }) } const startFileOperation = (title: string, description?: string) => { return progress.startOperation({ title, description: description || 'Processing file...', total: 100, completed: 0, canCancel: true, canPause: false }) } const startSyncOperation = (title: string, description?: string) => { return progress.startOperation({ title, description: description || 'Synchronizing data...', total: 100, completed: 0, canCancel: false, canPause: false }) } return { ...progress, startBulkOperation, startFileOperation, startSyncOperation } }