diff --git a/frontend/src/components/FileExplorer.tsx b/frontend/src/components/FileExplorer.tsx index defb715..4da18e5 100644 --- a/frontend/src/components/FileExplorer.tsx +++ b/frontend/src/components/FileExplorer.tsx @@ -297,17 +297,16 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { } } - // Load files on path change - const loadFiles = useCallback(async (path?: string) => { + const loadFiles = useCallback(async (path?: string, silent: boolean = false) => { const targetPath = path ?? currentPath; - setLoading(true); + if (!silent) setLoading(true); try { const data = await api.listDirectory(targetPath); setFiles(data.files || []); } catch { - setFiles([]); + if (!silent) setFiles([]); } finally { - setLoading(false); + if (!silent) setLoading(false); } }, [currentPath]); @@ -332,10 +331,8 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { } }, [showMoveModal, loadFolderTree]); - // Keyboard shortcuts useEffect(() => { function handleKeyDown(e: KeyboardEvent) { - // Don't trigger shortcuts when typing in inputs if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) return; if (e.key === 'Backspace') { @@ -357,7 +354,6 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { return () => window.removeEventListener('keydown', handleKeyDown); }, [files, selectedFiles]); - // Close context menu on click outside useEffect(() => { const handleClickOutside = () => { setContextMenu(null); @@ -367,18 +363,6 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { return () => window.removeEventListener('click', handleClickOutside); }, []); - async function loadPinned() { - setLoading(true); - try { - const data = await api.listPinned(); - setFiles(data.items || []); - } catch { - setFiles([]); - } finally { - setLoading(false); - } - } - async function loadTrash() { setLoading(true); try { @@ -409,12 +393,10 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { } } - // Effect to keep pinned list loaded for the sidebar const [pinnedFiles, setPinnedFiles] = useState([]); useEffect(() => { if (pinnedExpanded) { api.listPinned().then(data => { - // Sort by pinnedOrder const items = data.items || []; items.sort((a: FileItem, b: FileItem) => { const idxA = pinnedOrder.indexOf(a.path); @@ -431,7 +413,6 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { async function handlePinnedDragEnd(sourcePath: string, destPath: string) { const newOrder = [...pinnedOrder]; - // Ensure all current pinned files are in order array const currentPaths = pinnedFiles.map(f => f.path); for (const p of currentPaths) { if (!newOrder.includes(p)) newOrder.push(p); @@ -469,7 +450,6 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { function handleFileClick(file: FileItem, e: React.MouseEvent) { if (renaming === file.path) return; if (e.shiftKey) { - // Multi-select with shift setSelectedFiles(prev => { const next = new Set(prev); if (next.has(file.path)) { @@ -480,7 +460,6 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { return next; }); } else { - // Single click to open/navigate if (file.is_dir) navigateToFolder(file); else setPreviewFile(file); } @@ -600,21 +579,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { async function handlePin(file: FileItem) { await api.togglePin(file.path); - loadFiles(); - if (pinnedExpanded) { - api.listPinned().then(data => { - const items = data.items || []; - items.sort((a: FileItem, b: FileItem) => { - const idxA = pinnedOrder.indexOf(a.path); - const idxB = pinnedOrder.indexOf(b.path); - if (idxA === -1 && idxB === -1) return a.name.localeCompare(b.name); - if (idxA === -1) return 1; - if (idxB === -1) return -1; - return idxA - idxB; - }); - setPinnedFiles(items); - }).catch(console.error); - } + loadFiles(undefined, true); } async function handleRename(path: string, originalName: string, isDir: boolean) { @@ -633,7 +598,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { await api.rename(path, finalName); setRenaming(null); setNewName(''); - loadFiles(); + loadFiles(undefined, true); } async function handleCreateFolder() { @@ -642,7 +607,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { await api.createFolder(folderPath); setShowNewFolder(false); setNewFolderName(''); - loadFiles(); + loadFiles(undefined, true); } async function handleRestore(file: FileItem) { @@ -661,14 +626,13 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { await Promise.all(movingFiles.map(src => api.move(src, moveModalPath === '.' ? '.' : moveModalPath))); setShowMoveModal(false); setMovingFiles([]); - loadFiles(); + loadFiles(undefined, true); setSelectedFiles(new Set()); } catch (err) { console.error(err); } } - // Drag and drop upload function handleDragOver(e: React.DragEvent) { if (draggedFile) return; if (!e.dataTransfer.types.includes('Files')) return; @@ -711,7 +675,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { setDraggedFile(null); try { await api.move(sourcePath, file.path); - loadFiles(); + loadFiles(undefined, true); } catch (err) { console.error(err); } @@ -741,7 +705,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { if (draggedFile) return; const droppedFiles = Array.from(e.dataTransfer.files); await Promise.all(droppedFiles.map(file => uploadFile(file))); - loadFiles(); + loadFiles(undefined, true); } async function handleFileUpload(e: React.ChangeEvent) { @@ -749,10 +713,9 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { if (!uploadFiles) return; await Promise.all(Array.from(uploadFiles).map(file => uploadFile(file))); e.target.value = ''; - loadFiles(); + loadFiles(undefined, true); } - // Breadcrumb path segments const pathSegments = currentPath === '.' ? [] : currentPath.split('/'); function formatSize(bytes: number): string { @@ -851,7 +814,6 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { let displayFiles = searchResults !== null ? searchResults : files; displayFiles = displayFiles.filter(f => f.name !== '.uploads').sort((a, b) => { - // Directories always first regardless of sort type if (a.is_dir !== b.is_dir) return a.is_dir ? -1 : 1; let cmp = 0; @@ -921,7 +883,6 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { return (
- {/* === SIDEBAR === */} - {/* === MAIN CONTENT === */}
- {/* Top Bar */}
- {/* Breadcrumbs */}
- {/* Search Bar */} {activeSection !== 'settings' && (
@@ -1140,7 +1093,6 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
)} - {/* Action Buttons */}
{activeSection === 'trash' && (
- {/* Dynamic Content Area */} {activeSection === 'settings' ? ( ) : activeSection === 'storage' ? ( @@ -1240,7 +1191,6 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { ) : (
- {/* File Area */}
- {/* Lasso selection box */} {selectionBox && (
)} - {/* Drag and drop overlay */} {dragOver && ( - {/* New Folder Input */} {showNewFolder && ( ) : viewMode === 'grid' ? ( - /* Grid View */
{groupedFiles.map((group, groupIdx) => ( @@ -1435,7 +1381,6 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
) : ( - /* List View */
{groupedFiles.map((group, groupIdx) => ( @@ -1518,7 +1463,6 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { )}
- {/* Bulk Actions Bar */} {selectedFiles.size > 0 && ( { const form = document.createElement('form'); form.method = 'POST'; @@ -1569,6 +1514,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { @@ -1609,7 +1557,6 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { )}
- {/* === CONTEXT MENU === */} {contextMenu && ( { - api.deleteFile(contextMenu.file.path).then(() => loadFiles()); setContextMenu(null); + api.deleteFile(contextMenu.file.path).then(() => loadFiles(undefined, true)); }} /> diff --git a/frontend/src/components/LoginPage.tsx b/frontend/src/components/LoginPage.tsx index d01495a..35054fb 100644 --- a/frontend/src/components/LoginPage.tsx +++ b/frontend/src/components/LoginPage.tsx @@ -2,7 +2,7 @@ import { useState } from 'react'; import { motion } from 'framer-motion'; -import { HardDrive, Lock, User, LogIn, Shield } from 'lucide-react'; +import { HardDrive, Lock, User, LogIn, Shield, Eye, EyeOff } from 'lucide-react'; import api from '@/lib/api'; interface LoginPageProps { @@ -12,6 +12,7 @@ interface LoginPageProps { export default function LoginPage({ onSuccess }: LoginPageProps) { const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); + const [showPassword, setShowPassword] = useState(false); const [totpCode, setTotpCode] = useState(''); const [error, setError] = useState(''); const [loading, setLoading] = useState(false); @@ -128,17 +129,25 @@ export default function LoginPage({ onSuccess }: LoginPageProps) { setPassword(e.target.value)} placeholder="Your password" - className="w-full pl-10 pr-4 py-3 rounded-xl text-sm outline-none transition-all" + className="w-full pl-10 pr-10 py-3 rounded-xl text-sm outline-none transition-all" style={{ backgroundColor: 'var(--color-bg-tertiary)', border: '1px solid var(--color-border)', color: 'var(--color-text-primary)', }} /> +