'use client'; import { useMemo, useCallback } from 'react'; import { X, CheckCircle, AlertCircle, Loader2, FileIcon, ImageIcon, FileAudio, FileVideo, RotateCcw } from 'lucide-react'; import { cn } from '@djangocfg/ui-core/lib'; import { Button, Progress, Badge, Tooltip, TooltipContent, TooltipTrigger, Skeleton, } from '@djangocfg/ui-core/components'; import { useT } from '@djangocfg/i18n'; import { formatFileSize, truncateFilename } from '../utils'; import type { UploadPreviewItemProps, AssetType, UploadStatus } from '../types'; const ASSET_ICONS: Record = { image: ImageIcon, audio: FileAudio, video: FileVideo, document: FileIcon, }; const REMOVABLE_STATUSES: UploadStatus[] = ['pending', 'uploading', 'error', 'aborted']; export function UploadPreviewItem({ item, onRemove, onRetry, showThumbnail = true, }: UploadPreviewItemProps) { const t = useT(); const { id, file, status, progress, previewUrl, error } = item; // Prepare labels const labels = useMemo(() => ({ uploaded: t('tools.upload.uploaded'), failed: t('tools.upload.failed'), cancel: t('tools.upload.cancel'), pending: t('tools.upload.pending'), retry: t('tools.upload.retry'), remove: t('tools.upload.remove'), }), [t]); // Prepare file info const assetType = useMemo((): AssetType => { if (file.type.startsWith('image/')) return 'image'; if (file.type.startsWith('audio/')) return 'audio'; if (file.type.startsWith('video/')) return 'video'; return 'document'; }, [file.type]); const fileSize = useMemo(() => formatFileSize(file.size), [file.size]); const shortName = useMemo(() => truncateFilename(file.name), [file.name]); const progressPercent = Math.round(progress); // Prepare visibility flags const Icon = ASSET_ICONS[assetType]; const canShowImagePreview = showThumbnail && !!previewUrl && assetType === 'image'; const canShowVideoPreview = showThumbnail && !!previewUrl && assetType === 'video'; const canRetry = (status === 'error' || status === 'aborted') && !!onRetry; const canRemove = REMOVABLE_STATUSES.includes(status) && !!onRemove; const isUploading = status === 'uploading'; const hasError = !!error; // Prepare styles const containerClassName = cn( 'flex items-center gap-3 p-3 rounded-lg border', 'bg-card', hasError && 'border-destructive/50' ); // Prepare tooltip label for remove button const removeTooltip = isUploading ? labels.cancel : labels.remove; // Handlers const handleRemove = useCallback(() => { onRemove?.(id); }, [id, onRemove]); const handleRetry = useCallback(() => { onRetry?.(id); }, [id, onRetry]); // Prepare status badge const statusBadge = useMemo(() => { switch (status) { case 'uploading': return ( ); case 'complete': return ( ); case 'error': return ( ); case 'aborted': return ( {labels.cancel} ); default: return ( {labels.pending} ); } }, [status, progressPercent, labels]); // Prepare thumbnail content const thumbnailContent = useMemo(() => { if (canShowImagePreview && previewUrl) { return {file.name}; } if (canShowVideoPreview && previewUrl) { return (