import React, { useState, useEffect } from 'react'; import { Card } from '../ui/card'; import { Button } from '../ui/button'; import { SearchInput } from '../ui/input'; import { FileNode } from '../../types'; import { LayoutGrid, List, Filter, MoreVertical, Download, Trash2, Folder, Music, Image as ImageIcon, File, CheckSquare, Square, Tag, ArrowUp, ArrowDown, Share2, Wand2, Loader2, Stamp } from 'lucide-react'; import { useToast } from '../../context/ToastContext'; import { storageService } from '../../services/storageService'; import { AutoMetadataDetectionModal } from '../library/AutoMetadataDetectionModal'; import { WatermarkSettingsModal } from '../library/WatermarkSettingsModal'; import { FileDetailsView } from '../views/FileDetailsView'; import { LoadingState } from '../ui/LoadingState'; import { logger } from '@/utils/logger'; type SortField = 'name' | 'size' | 'modified' | 'type'; type SortOrder = 'asc' | 'desc'; export const CloudFileBrowser: React.FC = () => { const { addToast } = useToast(); const [viewMode, setViewMode] = useState<'list' | 'grid'>('list'); const [searchQuery, setSearchQuery] = useState(''); const [selectedFiles, setSelectedFiles] = useState([]); const [_currentFolder, setCurrentFolder] = useState('Root'); const [files, setFiles] = useState<(FileNode & { tags?: string[] })[]>([]); const [loading, setLoading] = useState(true); // Navigation State const [selectedFileId, setSelectedFileId] = useState(null); // Sorting & Filtering const [sortField, setSortField] = useState('modified'); const [sortOrder, setSortOrder] = useState('desc'); const [activeTags, setActiveTags] = useState([]); const [availableTags] = useState(['Vocals', 'Bass', 'Drums', 'Project', 'Art', 'Legal', 'Reference', 'Stem', 'Raw']); // Modals const [showMetadataModal, setShowMetadataModal] = useState(false); const [showWatermarkModal, setShowWatermarkModal] = useState(false); useEffect(() => { const loadFiles = async () => { setLoading(true); try { const data = await storageService.listFiles(); setFiles(data); } catch (e) { logger.error('Error loading files', { error: e instanceof Error ? e.message : String(e), stack: e instanceof Error ? e.stack : undefined, }); } finally { setLoading(false); } }; loadFiles(); }, []); const handleSort = (field: SortField) => { if (sortField === field) { setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc'); } else { setSortField(field); setSortOrder('asc'); } }; const toggleTag = (tag: string) => { setActiveTags(prev => prev.includes(tag) ? prev.filter(t => t !== tag) : [...prev, tag]); }; const toggleSelection = (id: string) => { setSelectedFiles(prev => prev.includes(id) ? prev.filter(fid => fid !== id) : [...prev, id]); }; const selectAll = () => { if (selectedFiles.length === files.length) setSelectedFiles([]); else setSelectedFiles(files.map(f => f.id)); }; const handleFileClick = (file: FileNode) => { if (file.type === 'folder') { setCurrentFolder(file.name); addToast(`Navigated to ${file.name}`, 'info'); } else { setSelectedFileId(file.id); } }; const filteredFiles = files .filter(f => f.name.toLowerCase().includes(searchQuery.toLowerCase())) .filter(f => activeTags.length === 0 || f.tags?.some(t => activeTags.includes(t))) .sort((a, b) => { let valA: string | number = a[sortField] || ''; let valB: string | number = b[sortField] || ''; if (sortField === 'size') { // Mock size parsing for sort valA = parseInt(a.size || '0') || 0; valB = parseInt(b.size || '0') || 0; } if (valA < valB) return sortOrder === 'asc' ? -1 : 1; if (valA > valB) return sortOrder === 'asc' ? 1 : -1; return 0; }); const handleAction = (action: string) => { if (selectedFiles.length === 0) return; addToast(`${action} ${selectedFiles.length} items`, "success"); setSelectedFiles([]); }; if (selectedFileId) { return setSelectedFileId(null)} />; } // CRITIQUE FIX #20: Utiliser LoadingState standardisé pour cohérence UX if (loading) { return ( ); } return (
{/* Controls Bar */}
setSearchQuery(e.target.value)} />
{/* Tag Filter */}
{availableTags.slice(0, 5).map(tag => ( ))}
{selectedFiles.length > 0 && (
)}
Sort:
{/* File Content */}
{viewMode === 'list' ? (
{filteredFiles.map((file) => ( ))}
{selectedFiles.length === files.length && files.length > 0 ? : }
handleSort('name')}>Name Tags handleSort('size')}>Size handleSort('modified')}>Modified Actions
toggleSelection(file.id)} className="cursor-pointer text-gray-500 hover:text-white"> {selectedFiles.includes(file.id) ? : }
handleFileClick(file)}> {file.type === 'folder' && } {file.type === 'audio' && } {file.type === 'image' && } {['document', 'archive', 'project'].includes(file.type) && } {file.name}
{file.tags?.map(t => ( {t} ))}
{file.size} {file.modified}
{file.type === 'audio' && ( )}
) : (
{filteredFiles.map((file) => ( handleFileClick(file)} >
{ e.stopPropagation(); toggleSelection(file.id); }}> {selectedFiles.includes(file.id) ? : }
{file.type === 'folder' && } {file.type === 'audio' && } {file.type === 'image' && } {['document', 'archive', 'project'].includes(file.type) && }

{file.name}

{file.tags?.slice(0,2).map(t => {t})}
))}
)}
{/* Modals */} {showMetadataModal && ( f.id === selectedFileId)?.name || 'Selected File' : 'Scan Library'} onClose={() => setShowMetadataModal(false)} onApply={(data) => { addToast(`Applied: ${data.genre} - ${data.bpm}BPM`, 'success'); setShowMetadataModal(false); }} /> )} {showWatermarkModal && ( setShowWatermarkModal(false)} onSave={() => addToast("Watermark settings updated", 'success')} /> )}
); };