File size: 4,281 Bytes
16d4b20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
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<FileUploaderProps> = ({ 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<HTMLInputElement>) => {
    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 (
    <div
      onDragOver={handleDragOver}
      onDragLeave={handleDragLeave}
      onDrop={handleDrop}
      className={`
        relative border-2 border-dashed rounded-xl p-8 text-center transition-all duration-200
        ${disabled ? 'opacity-50 cursor-not-allowed border-gray-200 bg-gray-50' : 'cursor-pointer'}
        ${isDragging ? 'border-yellow-400 bg-yellow-50' : 'border-gray-300 hover:border-yellow-400 hover:bg-gray-50'}
      `}
    >
      <input
        type="file"
        multiple
        onChange={handleFileInput}
        disabled={disabled}
        className="absolute inset-0 w-full h-full opacity-0 cursor-pointer disabled:cursor-not-allowed"
      />
      
      <div className="flex flex-col items-center justify-center space-y-3 pointer-events-none">
        <div className={`p-4 rounded-full ${isDragging ? 'bg-yellow-100 text-yellow-600' : 'bg-gray-100 text-gray-500'}`}>
          <UploadCloud className="w-8 h-8" />
        </div>
        <div>
          <p className="text-lg font-medium text-gray-700">
            {isDragging ? 'Drop files here' : 'Drag & drop files or click to browse'}
          </p>
          <p className="text-sm text-gray-500 mt-1">
            Files will be renamed: <code>timestamp-filename.ext</code>
          </p>
        </div>
      </div>
    </div>
  );
};