import React, { useCallback, useState } from 'react'; import { UploadCloud } from 'lucide-react'; import { FileItem, UploadStatus } from '../types'; const generateId = () => Math.random().toString(36).substring(2, 15); interface FileUploaderProps { onFilesAdded: (files: FileItem[]) => void; disabled: boolean; } /** * Beautifies filenames: * 1. Prepends current TIMESTAMP (Date.now()) * 2. Converts Vietnamese/Accents to English (e.g., "tài liệu" -> "tai-lieu") * 3. Removes special chars and spaces * Format: [TIMESTAMP]-[clean-name].[ext] */ const sanitizeFileName = (fileName: string): string => { const timestamp = Date.now(); // 1. Separate extension const lastDotIndex = fileName.lastIndexOf('.'); const name = lastDotIndex !== -1 ? fileName.substring(0, lastDotIndex) : fileName; const ext = lastDotIndex !== -1 ? fileName.substring(lastDotIndex) : ''; let cleanName = name; // 2. Remove EXISTING leading digits/timestamps to avoid double timestamps (e.g. 123_123_name) cleanName = cleanName.replace(/^\d+[-_.\s]*/, ''); // 3. Normalize Accents (Vietnamese, etc.) to ASCII cleanName = cleanName.normalize("NFD").replace(/[\u0300-\u036f]/g, "") .replace(/đ/g, 'd').replace(/Đ/g, 'D'); // 4. Replace non-alphanumeric characters with hyphens cleanName = cleanName.replace(/[^a-zA-Z0-9]/g, '-'); // 5. Collapse multiple hyphens and trim edges cleanName = cleanName.replace(/-+/g, '-').replace(/^-|-$/g, ''); // Fallback if name becomes empty if (cleanName.length === 0) cleanName = 'file'; // 6. Construct final name: timestamp-slug.ext return `${timestamp}-${cleanName}${ext}`.toLowerCase(); }; export const FileUploader: React.FC = ({ onFilesAdded, disabled }) => { const [isDragging, setIsDragging] = useState(false); const handleDragOver = useCallback((e: React.DragEvent) => { e.preventDefault(); if (!disabled) setIsDragging(true); }, [disabled]); const handleDragLeave = useCallback((e: React.DragEvent) => { e.preventDefault(); setIsDragging(false); }, []); const handleDrop = useCallback((e: React.DragEvent) => { e.preventDefault(); setIsDragging(false); if (disabled) return; if (e.dataTransfer.files && e.dataTransfer.files.length > 0) { processFiles(e.dataTransfer.files); } }, [disabled]); const handleFileInput = (e: React.ChangeEvent) => { if (e.target.files && e.target.files.length > 0) { processFiles(e.target.files); e.target.value = ''; // Reset input } }; const processFiles = (fileList: FileList) => { const newFiles: FileItem[] = Array.from(fileList).map(file => { const cleanPath = sanitizeFileName(file.name); return { id: generateId(), file, path: cleanPath, // Auto-formatted path with timestamp status: UploadStatus.IDLE }; }); onFilesAdded(newFiles); }; return (

{isDragging ? 'Drop files here' : 'Drag & drop files or click to browse'}

Files will be renamed: timestamp-filename.ext

); };