uploadv2 / components /UploadList.tsx
Twan07's picture
Upload 5 files
540f559 verified
import React from 'react';
import { FileItem, UploadStatus } from '../types';
import { FileText, Loader2, CheckCircle2, AlertTriangle, ExternalLink, X, Edit2 } from 'lucide-react';
interface UploadListProps {
files: FileItem[];
onRemove: (id: string) => void;
onPathChange: (id: string, newPath: string) => void;
}
export const UploadList: React.FC<UploadListProps> = ({ files, onRemove, onPathChange }) => {
if (files.length === 0) return null;
return (
<div className="bg-white/60 backdrop-blur-md rounded-2xl shadow-sm border border-gray-100 overflow-hidden ring-1 ring-gray-200/50">
<div className="px-6 py-4 border-b border-gray-100 flex justify-between items-center bg-gray-50/50">
<h3 className="font-semibold text-gray-700 flex items-center gap-2">
<div className="w-2 h-2 rounded-full bg-indigo-500" />
Upload Queue
<span className="bg-gray-200 text-gray-600 text-xs py-0.5 px-2 rounded-full ml-1">{files.length}</span>
</h3>
<span className="text-xs text-gray-400 font-medium">Auto-saving path changes</span>
</div>
<div className="max-h-[400px] overflow-y-auto custom-scrollbar divide-y divide-gray-100">
{files.map((item) => (
<div key={item.id} className="p-4 hover:bg-white transition-colors flex items-center gap-4 group relative">
{/* Icon Box */}
<div className={`
p-3 rounded-xl flex-shrink-0 transition-colors
${item.status === UploadStatus.SUCCESS ? 'bg-green-50 text-green-600' :
item.status === UploadStatus.ERROR ? 'bg-red-50 text-red-600' : 'bg-indigo-50 text-indigo-600'}
`}>
<FileText className="w-5 h-5" />
</div>
{/* File Info & Input */}
<div className="flex-1 min-w-0 space-y-1">
<div className="flex items-center gap-2">
<span className="font-medium text-sm text-gray-700 truncate max-w-[180px] md:max-w-xs" title={item.file.name}>
{item.file.name}
</span>
<span className="text-[10px] bg-gray-100 text-gray-500 px-1.5 py-0.5 rounded">
{(item.file.size / 1024).toFixed(1)} KB
</span>
</div>
{item.status === UploadStatus.IDLE && (
<div className="relative max-w-sm">
<input
type="text"
value={item.path}
onChange={(e) => onPathChange(item.id, e.target.value)}
className="w-full text-xs py-1.5 pl-2 pr-7 border border-transparent hover:border-gray-200 focus:border-indigo-400 focus:bg-white rounded bg-transparent transition-all outline-none text-gray-600 font-mono"
placeholder="path/to/file.ext"
/>
<Edit2 className="w-3 h-3 text-gray-300 absolute right-2 top-2 pointer-events-none" />
</div>
)}
{item.status === UploadStatus.UPLOADING && (
<div className="w-full bg-gray-100 rounded-full h-1.5 mt-2 overflow-hidden">
<div className="bg-indigo-500 h-1.5 rounded-full animate-[progress_1s_ease-in-out_infinite]" style={{width: '70%'}}></div>
</div>
)}
{item.status === UploadStatus.SUCCESS && (
<a
href={item.url}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-1 text-xs font-medium text-green-600 hover:text-green-700 hover:underline"
>
View on Hub <ExternalLink className="w-3 h-3" />
</a>
)}
{item.status === UploadStatus.ERROR && (
<span className="text-xs font-medium text-red-500 truncate block" title={item.error}>
Error: {item.error}
</span>
)}
</div>
{/* Actions / Status Icons */}
<div className="flex items-center gap-2 pl-2 border-l border-gray-50">
{item.status === UploadStatus.UPLOADING && (
<Loader2 className="w-5 h-5 text-indigo-500 animate-spin" />
)}
{item.status === UploadStatus.SUCCESS && (
<div className="flex items-center gap-1 text-green-600 bg-green-50 px-2 py-1 rounded-lg text-xs font-medium">
<CheckCircle2 className="w-4 h-4" />
<span>Done</span>
</div>
)}
{item.status === UploadStatus.ERROR && (
<div className="flex items-center gap-1 text-red-600 bg-red-50 px-2 py-1 rounded-lg text-xs font-medium">
<AlertTriangle className="w-4 h-4" />
<span>Failed</span>
</div>
)}
{item.status !== UploadStatus.UPLOADING && item.status !== UploadStatus.SUCCESS && (
<button
onClick={() => onRemove(item.id)}
className="p-2 text-gray-400 hover:text-red-500 hover:bg-red-50 rounded-lg transition-colors"
title="Remove file"
>
<X className="w-4 h-4" />
</button>
)}
</div>
</div>
))}
</div>
</div>
);
};