From ee36e16dae9923ae7d78ee88bbd786887db8486b Mon Sep 17 00:00:00 2001 From: Elijah Date: Sat, 23 May 2026 10:47:36 -0700 Subject: [PATCH] feat: add toast notifications for file actions --- frontend/src/components/FileExplorer.tsx | 70 ++++++++++++++++++++---- 1 file changed, 58 insertions(+), 12 deletions(-) diff --git a/frontend/src/components/FileExplorer.tsx b/frontend/src/components/FileExplorer.tsx index f745263..62d142d 100644 --- a/frontend/src/components/FileExplorer.tsx +++ b/frontend/src/components/FileExplorer.tsx @@ -7,7 +7,8 @@ import { ChevronRight, Grid3X3, List, Plus, Upload, LogOut, MoreVertical, Star, Download, Pencil, Copy, Move, FolderPlus, ArrowUp, X, Check, RefreshCw, Info, Link as LinkIcon, Image as ImageIcon, Film as FilmIcon, - Sun, Moon, Music as MusicIcon, Code as CodeIcon, Archive as ArchiveIcon, CornerDownRight + Sun, Moon, Music as MusicIcon, Code as CodeIcon, Archive as ArchiveIcon, CornerDownRight, + CheckCircle, AlertCircle } from 'lucide-react'; import api from '@/lib/api'; @@ -105,6 +106,12 @@ interface UploadProgress { error?: string; } +interface Toast { + id: number; + message: string; + type?: 'success' | 'error' | 'info'; +} + function getFileDisplayName(name: string, isDir: boolean) { if (isDir) return name; const dotIndex = name.lastIndexOf('.'); @@ -236,6 +243,15 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { const [sortAsc, setSortAsc] = useState(true); const [draggedFile, setDraggedFile] = useState(null); const [uploads, setUploads] = useState([]); + const [toasts, setToasts] = useState([]); + + function showToast(message: string, type: 'success' | 'error' | 'info' = 'success') { + const id = Date.now() + Math.random(); + setToasts(prev => [...prev, { id, message, type }]); + setTimeout(() => { + setToasts(prev => prev.filter(t => t.id !== id)); + }, 3000); + } const [showTrashModal, setShowTrashModal] = useState(false); const [showMoveModal, setShowMoveModal] = useState(false); const [movingFiles, setMovingFiles] = useState([]); @@ -599,17 +615,21 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { async function deleteSelected() { if (selectedFiles.size === 0) return; + if (!confirm(`Move ${selectedFiles.size} items to trash?`)) return; + const count = selectedFiles.size; for (const path of selectedFiles) { await api.deleteFile(path); } setSelectedFiles(new Set()); - loadFiles(); + loadFiles(undefined, true); + showToast(`Deleted ${count} item${count > 1 ? 's' : ''}`); } async function handlePin(file: FileItem) { await api.togglePin(file.path); loadFiles(undefined, true); setPinnedRefreshCounter(c => c + 1); + showToast(file.is_pinned ? 'Unpinned item' : 'Pinned item'); } async function handleRename(path: string, originalName: string, isDir: boolean) { @@ -629,6 +649,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { setRenaming(null); setNewName(''); loadFiles(undefined, true); + showToast(`Renamed to ${finalName}`); } async function handleCreateFolder() { @@ -643,11 +664,13 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { async function handleRestore(file: FileItem) { await api.restoreFromTrash(file.path); loadTrash(); + showToast(`Restored ${file.name}`); } async function handleEmptyTrash() { await api.emptyTrash(); loadTrash(); + showToast('Trash emptied'); } async function handleMoveExecute() { @@ -658,6 +681,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { setMovingFiles([]); loadFiles(undefined, true); setSelectedFiles(new Set()); + showToast(`Moved ${movingFiles.length} item${movingFiles.length > 1 ? 's' : ''}`); } catch (err) { console.error(err); } @@ -706,6 +730,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { try { await api.move(sourcePath, file.path); loadFiles(undefined, true); + showToast('Item moved'); } catch (err) { console.error(err); } @@ -1553,15 +1578,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { @@ -1676,8 +1693,13 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { label="Move to Trash" danger onClick={() => { + if (confirm(`Move ${contextMenu.file.name} to trash?`)) { + api.deleteFile(contextMenu.file.path).then(() => { + loadFiles(undefined, true); + showToast(`Deleted ${contextMenu.file.name}`); + }); + } setContextMenu(null); - api.deleteFile(contextMenu.file.path).then(() => loadFiles(undefined, true)); }} /> @@ -1716,6 +1738,30 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { )} + {/* Toast Notifications */} +
+ + {toasts.map(toast => ( + + {toast.type === 'success' && } + {toast.type === 'error' && } + {toast.type === 'info' && } + {toast.message} + + ))} + +
+ {/* Upload Progress Panel */} {uploads.length > 0 && (