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, 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-kodo-content-dim 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')} /> )}
); };