Просмотр исходного кода

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.

maziggy 7 месяцев назад
Родитель
Сommit
c2048d2bab

+ 5 - 0
CHANGELOG.md

@@ -9,6 +9,11 @@ All notable changes to Bambuddy will be documented in this file.
   - 8 basic colors shown by default (White, Black, Red, Blue, Green, Yellow, Orange, Gray)
   - Click "+" to expand 24 additional colors (Cyan, Magenta, Purple, Pink, Brown, Beige, Navy, Teal, Lime, Gold, Silver, Maroon, Olive, Coral, Salmon, Turquoise, Violet, Indigo, Chocolate, Tan, Slate, Charcoal, Ivory, Cream)
   - Click "-" to collapse back to basic colors
+- **File Manager Sorting** - Printer file manager now has sorting options:
+  - Sort by name (A-Z or Z-A)
+  - Sort by size (smallest or largest first)
+  - Sort by date (oldest or newest first)
+  - Directories always sorted first
 
 ### Fixed
 - **User preset AMS configuration** - Fixed user presets (inheriting from Bambu presets) showing empty fields in Bambu Studio after configuration:

+ 1 - 0
frontend/src/api/client.ts

@@ -1470,6 +1470,7 @@ export const api = {
         is_directory: boolean;
         size: number;
         path: string;
+        mtime?: string;
       }>;
     }>(`/printers/${printerId}/files?path=${encodeURIComponent(path)}`),
   getPrinterFileDownloadUrl: (printerId: number, path: string) =>

+ 53 - 2
frontend/src/components/FileManagerModal.tsx

@@ -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);

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
static/assets/index-BQHdfJWP.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
static/assets/index-CMAdGpGJ.css


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
static/assets/index-Sa64ulbW.css


+ 2 - 2
static/index.html

@@ -23,8 +23,8 @@
 
     <!-- Splash screens for iOS -->
     <link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
-    <script type="module" crossorigin src="/assets/index-CO2y-9S4.js"></script>
-    <link rel="stylesheet" crossorigin href="/assets/index-CMAdGpGJ.css">
+    <script type="module" crossorigin src="/assets/index-BQHdfJWP.js"></script>
+    <link rel="stylesheet" crossorigin href="/assets/index-Sa64ulbW.css">
   </head>
   <body>
     <div id="root"></div>

Некоторые файлы не были показаны из-за большого количества измененных файлов