/** * Offline Queue Manager Component * Action 2.5.1.4: Add UI for offline queue management * * Displays queued requests and allows users to view details, remove requests, or clear the queue */ import { useEffect, useState } from 'react'; import { offlineQueue, type QueuedRequest } from '@/services/offlineQueue'; import { Dialog } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Trash2, X, Clock, AlertCircle, CheckCircle2 } from 'lucide-react'; import { cn } from '@/lib/utils'; import { logger } from '@/utils/logger'; interface OfflineQueueManagerProps { open: boolean; onClose: () => void; } /** * Format timestamp to readable date/time */ function formatTimestamp(timestamp: number): string { const date = new Date(timestamp); return date.toLocaleString(); } /** * Format request method and URL for display */ function formatRequest(request: QueuedRequest): string { const method = request.config.method?.toUpperCase() || 'UNKNOWN'; const url = request.config.url || 'Unknown URL'; return `${method} ${url}`; } /** * Get priority badge color */ function getPriorityColor(priority: QueuedRequest['priority']): string { switch (priority) { case 'high': return 'bg-destructive/20 text-destructive border-destructive/30'; case 'normal': return 'bg-muted/20 text-muted-foreground border-border/30'; case 'low': return 'bg-muted/30 text-muted-foreground border-border/50'; default: return 'bg-muted/30 text-muted-foreground border-border/50'; } } export function OfflineQueueManager({ open, onClose, }: OfflineQueueManagerProps) { const [queue, setQueue] = useState([]); const [isRemoving, setIsRemoving] = useState(null); const [isClearing, setIsClearing] = useState(false); // Update queue when dialog opens or periodically useEffect(() => { if (!open) return; const updateQueue = () => { setQueue(offlineQueue.getQueue()); }; // Update immediately updateQueue(); // Update every second while dialog is open const interval = setInterval(updateQueue, 1000); return () => clearInterval(interval); }, [open]); const handleRemoveRequest = async (requestId: string) => { setIsRemoving(requestId); try { await offlineQueue.removeRequest(requestId); setQueue(offlineQueue.getQueue()); } catch (error) { logger.error('Failed to remove request', { error: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, requestId, }); } finally { setIsRemoving(null); } }; const handleClearQueue = async () => { setIsClearing(true); try { await offlineQueue.clearQueue(); setQueue([]); onClose(); } catch (error) { logger.error('Failed to clear queue', { error: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, }); } finally { setIsClearing(false); } }; return (
{/* Queue Summary */}
{queue.length === 0 ? 'No queued requests' : `${queue.length} ${queue.length === 1 ? 'request' : 'requests'} queued`}
{queue.length > 0 && ( )}
{/* Queue List */} {queue.length === 0 ? (

All requests have been processed

) : (
{queue.map((request) => (
{/* Request Method and URL */}
{formatRequest(request)}
{/* Metadata */}
{/* Priority Badge */} {request.priority} {/* Timestamp */} {formatTimestamp(request.timestamp)} {/* Retry Count */} {request.retryCount > 0 && ( {request.retryCount} retry {request.retryCount > 1 ? 'ies' : ''} )}
{/* Remove Button */}
))}
)} {/* Info Message */} {queue.length > 0 && (

Queued requests will be automatically processed when you're back online. You can remove individual requests or clear the entire queue.

)}
); }