veza/apps/web/src/components/upload/FilePreviewCard.tsx
senke dc88ea6805 ui(tokens): migrate text-[10px] to text-xs across 23 components
Replace all arbitrary text-[10px] / text-[9px] with the standard Tailwind
text-xs token. Also migrates nearby arbitrary width values where applicable
(max-w-[200px] → max-w-48, max-w-[120px] → max-w-32, h-[1px] → h-px).

Only documented exceptions remain: avatar xs (text-[10px] for w-6 h-6
initials) and badge JSDoc reference.

Affected areas: admin views, marketplace, player, settings, chat, upload,
education, commerce, library, PWA, search, navbar, user card.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-08 22:47:19 +01:00

106 lines
3 KiB
TypeScript

import React from 'react';
import { Card } from '../ui/card';
import { UploadProgressBar } from './UploadProgressBar';
import {
FileAudio,
FileImage,
FolderArchive,
File,
CheckCircle,
AlertCircle,
} from 'lucide-react';
export interface UploadFile {
id: string;
file: File;
progress: number;
status: 'uploading' | 'paused' | 'completed' | 'error' | 'processing';
previewUrl?: string;
}
interface FilePreviewCardProps {
fileData: UploadFile;
onPause: () => void;
onResume: () => void;
onCancel: () => void;
}
export const FilePreviewCard: React.FC<FilePreviewCardProps> = ({
fileData,
onPause,
onResume,
onCancel,
}) => {
const { file, progress, status, previewUrl } = fileData;
const formatSize = (bytes: number) => {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
};
const getIcon = () => {
if (file.type.startsWith('image/'))
return <FileImage className="w-6 h-6 text-primary" />;
if (file.type.startsWith('audio/'))
return <FileAudio className="w-6 h-6 text-kodo-steel" />;
if (file.type.includes('zip') || file.type.includes('compressed'))
return <FolderArchive className="w-6 h-6 text-warning" />;
return <File className="w-6 h-6 text-kodo-content-dim" />;
};
return (
<Card
variant="glass"
className="p-4 flex items-center gap-4 border-l-4 border-l-muted hover:border-l-primary transition-all"
>
{/* Thumbnail / Icon */}
<div className="w-12 h-12 rounded-lg bg-black/40 flex items-center justify-center overflow-hidden flex-shrink-0 border border-white/5">
{previewUrl && file.type.startsWith('image/') ? (
<img
src={previewUrl}
alt="Preview"
className="w-full h-full object-cover"
/>
) : (
getIcon()
)}
</div>
{/* Info & Progress */}
<div className="flex-1 min-w-0">
<div className="flex justify-between items-start mb-1">
<h4
className="font-bold text-white text-sm truncate pr-2"
title={file.name}
>
{file.name}
</h4>
<span className="text-xs text-kodo-content-dim font-mono flex-shrink-0">
{formatSize(file.size)}
</span>
</div>
{status === 'completed' ? (
<div className="flex items-center gap-2 text-xs text-kodo-lime font-bold">
<CheckCircle className="w-3 h-3" /> Ready
</div>
) : status === 'error' ? (
<div className="flex items-center gap-2 text-xs text-destructive font-bold">
<AlertCircle className="w-3 h-3" /> Failed
</div>
) : (
<UploadProgressBar
progress={progress}
status={status}
onPause={onPause}
onResume={onResume}
onCancel={onCancel}
/>
)}
</div>
</Card>
);
};