mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-22 03:49:18 +02:00
Add File Manager and enhanced Queue features
File Manager: - Add full File Manager page for browsing local library files - Implement folder hierarchy with create/rename/delete operations - Add file uploads with drag-and-drop support - Enable adding sliced files directly to print queue - Add linked folders with external path mounting Queue Enhancements: - Add plate selection for multi-plate 3MF files - Add print options (bed levelling, flow calibration, vibration calibration, layer inspection, timelapse, use AMS) - Fix print_count increment to only occur on actual prints Testing: - Add backend integration tests for library API (15 tests) - Add backend integration tests for queue print options (8 tests) - Add frontend tests for EditQueueItemModal (10 tests) - Suppress console noise in frontend test suite Closes #94
This commit is contained in:
@@ -590,6 +590,9 @@ export interface AppSettings {
|
||||
ha_enabled: boolean;
|
||||
ha_url: string;
|
||||
ha_token: string;
|
||||
// File Manager / Library settings
|
||||
library_archive_mode: 'always' | 'never' | 'ask';
|
||||
library_disk_warning_gb: number;
|
||||
}
|
||||
|
||||
export type AppSettingsUpdate = Partial<AppSettings>;
|
||||
@@ -847,6 +850,14 @@ export interface PrintQueueItem {
|
||||
auto_off_after: boolean;
|
||||
manual_start: boolean; // Requires manual trigger to start (staged)
|
||||
ams_mapping: number[] | null; // AMS slot mapping for multi-color prints
|
||||
plate_id: number | null; // Plate ID for multi-plate 3MF files
|
||||
// Print options
|
||||
bed_levelling: boolean;
|
||||
flow_cali: boolean;
|
||||
vibration_cali: boolean;
|
||||
layer_inspect: boolean;
|
||||
timelapse: boolean;
|
||||
use_ams: boolean;
|
||||
status: 'pending' | 'printing' | 'completed' | 'failed' | 'skipped' | 'cancelled';
|
||||
started_at: string | null;
|
||||
completed_at: string | null;
|
||||
@@ -866,6 +877,14 @@ export interface PrintQueueItemCreate {
|
||||
auto_off_after?: boolean;
|
||||
manual_start?: boolean; // Requires manual trigger to start (staged)
|
||||
ams_mapping?: number[] | null; // AMS slot mapping for multi-color prints
|
||||
plate_id?: number | null; // Plate ID for multi-plate 3MF files
|
||||
// Print options
|
||||
bed_levelling?: boolean;
|
||||
flow_cali?: boolean;
|
||||
vibration_cali?: boolean;
|
||||
layer_inspect?: boolean;
|
||||
timelapse?: boolean;
|
||||
use_ams?: boolean;
|
||||
}
|
||||
|
||||
export interface PrintQueueItemUpdate {
|
||||
@@ -876,6 +895,14 @@ export interface PrintQueueItemUpdate {
|
||||
auto_off_after?: boolean;
|
||||
manual_start?: boolean;
|
||||
ams_mapping?: number[];
|
||||
plate_id?: number | null; // Plate ID for multi-plate 3MF files
|
||||
// Print options
|
||||
bed_levelling?: boolean;
|
||||
flow_cali?: boolean;
|
||||
vibration_cali?: boolean;
|
||||
layer_inspect?: boolean;
|
||||
timelapse?: boolean;
|
||||
use_ams?: boolean;
|
||||
}
|
||||
|
||||
// MQTT Logging types
|
||||
@@ -2443,6 +2470,75 @@ export const api = {
|
||||
|
||||
// System Info
|
||||
getSystemInfo: () => request<SystemInfo>('/system/info'),
|
||||
|
||||
// Library (File Manager)
|
||||
getLibraryFolders: () => request<LibraryFolderTree[]>('/library/folders'),
|
||||
createLibraryFolder: (data: LibraryFolderCreate) =>
|
||||
request<LibraryFolder>('/library/folders', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
updateLibraryFolder: (id: number, data: LibraryFolderUpdate) =>
|
||||
request<LibraryFolder>(`/library/folders/${id}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
deleteLibraryFolder: (id: number) =>
|
||||
request<{ status: string; message: string }>(`/library/folders/${id}`, { method: 'DELETE' }),
|
||||
getLibraryFoldersByProject: (projectId: number) =>
|
||||
request<LibraryFolder[]>(`/library/folders/by-project/${projectId}`),
|
||||
getLibraryFoldersByArchive: (archiveId: number) =>
|
||||
request<LibraryFolder[]>(`/library/folders/by-archive/${archiveId}`),
|
||||
|
||||
getLibraryFiles: (folderId?: number | null, includeRoot = true) => {
|
||||
const params = new URLSearchParams();
|
||||
if (folderId !== undefined && folderId !== null) {
|
||||
params.set('folder_id', String(folderId));
|
||||
}
|
||||
params.set('include_root', String(includeRoot));
|
||||
return request<LibraryFileListItem[]>(`/library/files?${params}`);
|
||||
},
|
||||
getLibraryFile: (id: number) => request<LibraryFile>(`/library/files/${id}`),
|
||||
uploadLibraryFile: async (file: File, folderId?: number | null): Promise<LibraryFileUploadResponse> => {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
const params = folderId ? `?folder_id=${folderId}` : '';
|
||||
const response = await fetch(`${API_BASE}/library/files${params}`, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
});
|
||||
if (!response.ok) {
|
||||
const error = await response.json().catch(() => ({}));
|
||||
throw new Error(error.detail || `HTTP ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
},
|
||||
updateLibraryFile: (id: number, data: LibraryFileUpdate) =>
|
||||
request<LibraryFile>(`/library/files/${id}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
deleteLibraryFile: (id: number) =>
|
||||
request<{ status: string; message: string }>(`/library/files/${id}`, { method: 'DELETE' }),
|
||||
getLibraryFileDownloadUrl: (id: number) => `${API_BASE}/library/files/${id}/download`,
|
||||
getLibraryFileThumbnailUrl: (id: number) => `${API_BASE}/library/files/${id}/thumbnail`,
|
||||
getLibraryFileGcodeUrl: (id: number) => `${API_BASE}/library/files/${id}/gcode`,
|
||||
moveLibraryFiles: (fileIds: number[], folderId: number | null) =>
|
||||
request<{ status: string; moved: number }>('/library/files/move', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ file_ids: fileIds, folder_id: folderId }),
|
||||
}),
|
||||
bulkDeleteLibrary: (fileIds: number[], folderIds: number[]) =>
|
||||
request<{ deleted_files: number; deleted_folders: number }>('/library/bulk-delete', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ file_ids: fileIds, folder_ids: folderIds }),
|
||||
}),
|
||||
getLibraryStats: () => request<LibraryStats>('/library/stats'),
|
||||
addLibraryFilesToQueue: (fileIds: number[]) =>
|
||||
request<AddToQueueResponse>('/library/files/add-to-queue', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ file_ids: fileIds }),
|
||||
}),
|
||||
};
|
||||
|
||||
// AMS History types
|
||||
@@ -2536,6 +2632,137 @@ export interface SystemInfo {
|
||||
};
|
||||
}
|
||||
|
||||
// Library (File Manager) types
|
||||
export interface LibraryFolderTree {
|
||||
id: number;
|
||||
name: string;
|
||||
parent_id: number | null;
|
||||
project_id: number | null;
|
||||
archive_id: number | null;
|
||||
project_name: string | null;
|
||||
archive_name: string | null;
|
||||
file_count: number;
|
||||
children: LibraryFolderTree[];
|
||||
}
|
||||
|
||||
export interface LibraryFolder {
|
||||
id: number;
|
||||
name: string;
|
||||
parent_id: number | null;
|
||||
project_id: number | null;
|
||||
archive_id: number | null;
|
||||
project_name: string | null;
|
||||
archive_name: string | null;
|
||||
file_count: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface LibraryFolderCreate {
|
||||
name: string;
|
||||
parent_id?: number | null;
|
||||
project_id?: number | null;
|
||||
archive_id?: number | null;
|
||||
}
|
||||
|
||||
export interface LibraryFolderUpdate {
|
||||
name?: string;
|
||||
parent_id?: number | null;
|
||||
project_id?: number | null; // 0 to unlink
|
||||
archive_id?: number | null; // 0 to unlink
|
||||
}
|
||||
|
||||
export interface LibraryFileDuplicate {
|
||||
id: number;
|
||||
filename: string;
|
||||
folder_id: number | null;
|
||||
folder_name: string | null;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface LibraryFile {
|
||||
id: number;
|
||||
folder_id: number | null;
|
||||
folder_name: string | null;
|
||||
project_id: number | null;
|
||||
project_name: string | null;
|
||||
filename: string;
|
||||
file_path: string;
|
||||
file_type: string;
|
||||
file_size: number;
|
||||
file_hash: string | null;
|
||||
thumbnail_path: string | null;
|
||||
metadata: Record<string, unknown> | null;
|
||||
print_count: number;
|
||||
last_printed_at: string | null;
|
||||
notes: string | null;
|
||||
duplicates: LibraryFileDuplicate[] | null;
|
||||
duplicate_count: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface LibraryFileListItem {
|
||||
id: number;
|
||||
folder_id: number | null;
|
||||
filename: string;
|
||||
file_type: string;
|
||||
file_size: number;
|
||||
thumbnail_path: string | null;
|
||||
print_count: number;
|
||||
duplicate_count: number;
|
||||
created_at: string;
|
||||
print_name: string | null;
|
||||
print_time_seconds: number | null;
|
||||
filament_used_grams: number | null;
|
||||
}
|
||||
|
||||
export interface LibraryFileUpdate {
|
||||
folder_id?: number | null;
|
||||
project_id?: number | null;
|
||||
notes?: string | null;
|
||||
}
|
||||
|
||||
export interface LibraryFileUploadResponse {
|
||||
id: number;
|
||||
filename: string;
|
||||
file_type: string;
|
||||
file_size: number;
|
||||
thumbnail_path: string | null;
|
||||
duplicate_of: number | null;
|
||||
metadata: Record<string, unknown> | null;
|
||||
}
|
||||
|
||||
export interface LibraryStats {
|
||||
total_files: number;
|
||||
total_folders: number;
|
||||
total_size_bytes: number;
|
||||
files_by_type: Record<string, number>;
|
||||
total_prints: number;
|
||||
disk_free_bytes: number;
|
||||
disk_total_bytes: number;
|
||||
disk_used_bytes: number;
|
||||
}
|
||||
|
||||
// Library Queue types
|
||||
export interface AddToQueueResult {
|
||||
file_id: number;
|
||||
filename: string;
|
||||
queue_item_id: number;
|
||||
archive_id: number;
|
||||
}
|
||||
|
||||
export interface AddToQueueError {
|
||||
file_id: number;
|
||||
filename: string;
|
||||
error: string;
|
||||
}
|
||||
|
||||
export interface AddToQueueResponse {
|
||||
added: AddToQueueResult[];
|
||||
errors: AddToQueueError[];
|
||||
}
|
||||
|
||||
// Discovery types
|
||||
export interface DiscoveredPrinter {
|
||||
serial: string;
|
||||
|
||||
Reference in New Issue
Block a user