Added sort dropdown to printer file manager with options for:

- Name (A-Z / Z-A)
- Size (smallest / largest first)
- Date (oldest / newest first)

Directories are always sorted first regardless of sort option.
This commit is contained in:
maziggy
2026-01-20 12:25:10 +01:00
parent 494da46b09
commit c2048d2bab
7 changed files with 444 additions and 387 deletions
+53 -2
View File
@@ -15,6 +15,7 @@ import {
FileText,
Image,
Search,
ArrowUpDown,
} from 'lucide-react';
import { api } from '../api/client';
import { Button } from './Button';
@@ -66,6 +67,17 @@ function getFileIcon(filename: string, isDirectory: boolean) {
}
}
type SortOption = 'name-asc' | 'name-desc' | 'size-asc' | 'size-desc' | 'date-asc' | 'date-desc';
const SORT_OPTIONS: { value: SortOption; label: string }[] = [
{ value: 'name-asc', label: 'Name (A-Z)' },
{ value: 'name-desc', label: 'Name (Z-A)' },
{ value: 'size-asc', label: 'Size (smallest)' },
{ value: 'size-desc', label: 'Size (largest)' },
{ value: 'date-asc', label: 'Date (oldest)' },
{ value: 'date-desc', label: 'Date (newest)' },
];
export function FileManagerModal({ printerId, printerName, onClose }: FileManagerModalProps) {
const { showToast } = useToast();
const queryClient = useQueryClient();
@@ -73,6 +85,7 @@ export function FileManagerModal({ printerId, printerName, onClose }: FileManage
const [selectedFile, setSelectedFile] = useState<string | null>(null);
const [searchQuery, setSearchQuery] = useState('');
const [fileToDelete, setFileToDelete] = useState<string | null>(null);
const [sortBy, setSortBy] = useState<SortOption>('name-asc');
// Close on Escape key
useEffect(() => {
@@ -206,6 +219,20 @@ export function FileManagerModal({ printerId, printerName, onClose }: FileManage
className="w-40 pl-8 pr-3 py-1.5 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white text-sm focus:border-bambu-green focus:outline-none"
/>
</div>
<div className="relative flex items-center gap-1">
<ArrowUpDown className="w-4 h-4 text-bambu-gray" />
<select
value={sortBy}
onChange={(e) => setSortBy(e.target.value as SortOption)}
className="appearance-none bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white text-sm py-1.5 pl-2 pr-6 focus:border-bambu-green focus:outline-none cursor-pointer"
>
{SORT_OPTIONS.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
</div>
<Button
variant="secondary"
size="sm"
@@ -240,15 +267,39 @@ export function FileManagerModal({ printerId, printerName, onClose }: FileManage
</div>
) : (
<div className="space-y-1">
{/* Filter and sort: directories first, then files */}
{/* Filter and sort: directories first, then files with selected sort */}
{[...data.files]
.filter((file) =>
!searchQuery || file.name.toLowerCase().includes(searchQuery.toLowerCase())
)
.sort((a, b) => {
// Directories always first
if (a.is_directory && !b.is_directory) return -1;
if (!a.is_directory && b.is_directory) return 1;
return a.name.localeCompare(b.name);
// Apply selected sort within same type
switch (sortBy) {
case 'name-asc':
return a.name.localeCompare(b.name);
case 'name-desc':
return b.name.localeCompare(a.name);
case 'size-asc':
return a.size - b.size;
case 'size-desc':
return b.size - a.size;
case 'date-asc': {
const aTime = a.mtime ? new Date(a.mtime).getTime() : 0;
const bTime = b.mtime ? new Date(b.mtime).getTime() : 0;
return aTime - bTime;
}
case 'date-desc': {
const aTime = a.mtime ? new Date(a.mtime).getTime() : 0;
const bTime = b.mtime ? new Date(b.mtime).getTime() : 0;
return bTime - aTime;
}
default:
return a.name.localeCompare(b.name);
}
})
.map((file) => {
const FileIcon = getFileIcon(file.name, file.is_directory);