import React, { useState, memo } from 'react'; import type { DownloadQueueItem } from '../types'; import TrashIcon from './TrashIcon'; import { TruncatedPath } from './TruncatedPath'; import { FolderPathDisplay } from './FolderPathDisplay'; import { CopyButtonWithModal } from './CopyButtonWithModal'; interface DownloadQueueItemProps { item: DownloadQueueItem; queuePosition?: number; onCancel: (jobId: string) => void; } const DownloadQueueItem: React.FC = ({ item, queuePosition, onCancel }) => { const [isExpanded, setIsExpanded] = useState(true); const isProcessing = item.status === 'processing'; const isWaiting = item.status === 'waiting' || item.status === 'pending'; const hasProgress = item.progressData && item.progressData.files_status; // Format GE ID and Lang (uppercase lang) const geIdAndLang = item.name.includes(' ') ? item.name.split(' ').map((part, idx) => idx === 1 ? part.toUpperCase() : part).join(' ') : item.name; // Get status color const getStatusColor = () => { if (isProcessing) return 'text-amber-400'; if (isWaiting) return 'text-blue-400'; return 'text-slate-400'; }; // Get status background const getStatusBg = () => { if (isProcessing) return 'bg-amber-900/30 border-amber-700/25'; if (isWaiting) return 'bg-blue-900/30 border-blue-700/25'; return 'bg-slate-900/30 border-slate-700/25'; }; // Extract paths based on mode const isSharing = item.mode === 'sharing'; const sourcePath = isSharing ? (item.sharingUrl || '') : (item.mongoDbPath || item.name.split(' - ')[1] || ''); const sourceLabel = isSharing ? 'Sharing Link' : 'Lezhin Disk'; // Extract destination path from item data const destinationPath = item.destinationPath || ''; // Format file size const formatSize = (bytes: number) => { if (bytes === 0) return ''; if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`; }; return (
{/* Header: GE ID, Queue Position, Cancel Button */}
{geIdAndLang} {queuePosition && ( <>
{queuePosition}
{isProcessing ? 'Đang xử lý' : 'Đang chờ'}
)} {!queuePosition && ( <> {isProcessing ? 'Đang xử lý' : 'Đang chờ'} )} {/* Progress summary */} {hasProgress && item.progressData && ( <> {item.progressData.current_file_index || 0}/{item.progressData.total_files || 0} files )}
{/* Expand/Collapse button */} {hasProgress && ( )} {/* Cancel Button */}
{/* Path Flow: Source → NAS Path */} {(sourcePath || destinationPath) && (
{/* Source Path (Lezhin Disk or Sharing Link) */} {sourcePath && (
)} {/* Arrow Icon */} {sourcePath && destinationPath && ( )} {/* NAS Path (Destination) - Show only folder name */} {destinationPath && (
)}
)} {/* Files Progress List */} {hasProgress && isExpanded && item.progressData?.files_status && (
{[...item.progressData.files_status].sort((a, b) => { // Natural sort by file name return a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' }); }).map((file, idx) => (
{/* File icon */} {file.is_folder ? '📁' : '📄'} {/* File name */} {file.name} {/* Status badge */} {file.status === 'completed' ? '✓ Hoàn tất' : file.status === 'downloading' ? '↓ Đang tải' : file.status === 'failed' ? '✗ Lỗi' : '⋯ Chờ'}
{/* Size/Progress info */}
{file.status === 'downloading' && file.downloaded !== undefined ? ( // Show: downloaded/total for files with known size, or just downloaded for folders file.total !== undefined && file.total > 0 ? ( {formatSize(file.downloaded)} / {formatSize(file.total)} ) : ( {formatSize(file.downloaded)} ) ) : file.status === 'completed' && file.size > 0 ? ( {formatSize(file.size)} ) : null}
{/* Progress bar (only for files with known size) */} {file.status === 'downloading' && !file.is_folder && file.progress !== undefined && (
)} {/* Folder progress bar (indeterminate - full bar with pulse animation) */} {file.status === 'downloading' && file.is_folder && file.downloaded !== undefined && (
{/* Full bar with pulse animation for folders */}
)}
))}
)}
); }; export default memo(DownloadQueueItem);