Merge pull request #4 from cadtoolbox/copilot/update-print-methods-file-manager

Replace FileManagerPage queue mutation with Schedule dialog
This commit is contained in:
Thomas Rambach
2026-02-03 21:38:45 -05:00
committed by GitHub
7 changed files with 117 additions and 112 deletions
@@ -459,8 +459,8 @@ describe('FileManagerPage', () => {
});
});
describe('add to queue', () => {
it('shows add to queue button for sliced files', async () => {
describe('schedule print', () => {
it('shows schedule print button for sliced files', async () => {
const user = userEvent.setup();
render(<FileManagerPage />);
@@ -472,7 +472,7 @@ describe('FileManagerPage', () => {
await user.click(screen.getByText('Select All'));
await waitFor(() => {
expect(screen.getByText(/Add to Queue/)).toBeInTheDocument();
expect(screen.getByText(/Schedule Print/)).toBeInTheDocument();
});
});
});
+1
View File
@@ -1848,6 +1848,7 @@ export default {
linkTo: 'Verknüpfen mit...',
linkToProjectOrArchive: 'Mit Projekt oder Archiv verknüpfen',
addToQueue: 'Zur Warteschlange',
schedulePrint: 'Druck planen',
generateThumbnail: 'Vorschaubild generieren',
generateThumbnails: 'Vorschaubilder generieren',
generateThumbnailsForMissing: 'Vorschaubilder für STL-Dateien ohne Vorschau generieren',
+1
View File
@@ -1848,6 +1848,7 @@ export default {
linkTo: 'Link to...',
linkToProjectOrArchive: 'Link to project or archive',
addToQueue: 'Add to Queue',
schedulePrint: 'Schedule Print',
generateThumbnail: 'Generate Thumbnail',
generateThumbnails: 'Generate Thumbnails',
generateThumbnailsForMissing: 'Generate thumbnails for STL files missing them',
+1
View File
@@ -1885,6 +1885,7 @@ export default {
linkTo: 'リンク先...',
printedCount: '{{count}}回印刷済み',
addToQueue: 'キューに追加',
schedulePrint: '印刷をスケジュール',
adding: '追加中...',
folderCreated: 'フォルダを作成しました',
folderDeleted: 'フォルダを削除しました',
+35 -33
View File
@@ -1015,7 +1015,7 @@ function FileCard({ file, isSelected, isMobile, onSelect, onDelete, onDownload,
title={!hasPermission('queue:create') ? t('fileManager.noPermissionAddToQueue') : undefined}
>
<Clock className="w-3.5 h-3.5" />
{t('fileManager.addToQueue')}
{t('fileManager.schedulePrint')}
</button>
)}
<button
@@ -1104,6 +1104,7 @@ export function FileManagerPage() {
const [deleteConfirm, setDeleteConfirm] = useState<{ type: 'file' | 'folder' | 'bulk'; id: number; count?: number } | null>(null);
const [printFile, setPrintFile] = useState<LibraryFileListItem | null>(null);
const [printMultiFile, setPrintMultiFile] = useState<LibraryFileListItem | null>(null);
const [scheduleFile, setScheduleFile] = useState<LibraryFileListItem | null>(null);
const [renameItem, setRenameItem] = useState<{ type: 'file' | 'folder'; id: number; name: string } | null>(null);
const [thumbnailVersions, setThumbnailVersions] = useState<Record<number, number>>({});
const [viewMode, setViewMode] = useState<'grid' | 'list'>(() => {
@@ -1353,31 +1354,6 @@ export function FileManagerPage() {
onError: (error: Error) => showToast(error.message, 'error'),
});
const addToQueueMutation = useMutation({
mutationFn: (fileIds: number[]) => api.addLibraryFilesToQueue(fileIds),
onSuccess: (result) => {
queryClient.invalidateQueries({ queryKey: ['library-files'] });
queryClient.invalidateQueries({ queryKey: ['queue'] });
queryClient.invalidateQueries({ queryKey: ['archives'] }); // Archives are created when adding to queue
setSelectedFiles([]);
if (result.added.length > 0 && result.errors.length === 0) {
showToast(
t('fileManager.toast.addedToQueue', { count: result.added.length }),
'success'
);
} else if (result.added.length > 0 && result.errors.length > 0) {
showToast(
t('fileManager.toast.addedToQueuePartial', { added: result.added.length, failed: result.errors.length }),
'success'
);
} else {
showToast(t('fileManager.toast.failedToAddToQueue', { error: result.errors[0]?.error || 'Unknown error' }), 'error');
}
},
onError: (error: Error) => showToast(error.message, 'error'),
});
const renameFileMutation = useMutation({
mutationFn: ({ id, filename }: { id: number; filename: string }) =>
api.updateLibraryFile(id, { filename }),
@@ -1859,12 +1835,15 @@ export function FileManagerPage() {
<Button
variant={selectedSlicedFiles.length === 1 ? 'secondary' : 'primary'}
size="sm"
onClick={() => addToQueueMutation.mutate(selectedSlicedFiles.map(f => f.id))}
disabled={addToQueueMutation.isPending || !hasPermission('queue:create')}
// Note: Schedule dialog (PrintModal) is designed for single file at a time
// but supports scheduling to multiple printers. This provides more control
// over scheduling options compared to the previous bulk queue mutation.
onClick={() => setScheduleFile(selectedSlicedFiles[0])}
disabled={!hasPermission('queue:create')}
title={!hasPermission('queue:create') ? t('fileManager.noPermissionAddToQueue') : undefined}
>
<Clock className="w-4 h-4 sm:mr-1" />
<span className="hidden sm:inline">{addToQueueMutation.isPending ? t('fileManager.adding') : `${t('fileManager.addToQueue')}${selectedSlicedFiles.length < selectedFiles.length ? ` (${selectedSlicedFiles.length})` : ''}`}</span>
<span className="hidden sm:inline">{`${t('fileManager.schedulePrint')}${selectedSlicedFiles.length < selectedFiles.length ? ` (${selectedSlicedFiles.length})` : ''}`}</span>
</Button>
)}
<Button
@@ -1963,7 +1942,10 @@ export function FileManagerPage() {
onSelect={handleFileSelect}
onDelete={(id) => setDeleteConfirm({ type: 'file', id })}
onDownload={handleDownload}
onAddToQueue={(id) => addToQueueMutation.mutate([id])}
onAddToQueue={(id) => {
const file = files?.find(f => f.id === id);
if (file) setScheduleFile(file);
}}
onPrint={setPrintFile}
onRename={(f) => setRenameItem({ type: 'file', id: f.id, name: f.filename })}
onGenerateThumbnail={(f) => singleThumbnailMutation.mutate(f.id)}
@@ -2068,14 +2050,18 @@ export function FileManagerPage() {
<Printer className="w-4 h-4" />
</button>
<button
onClick={() => hasPermission('queue:create') && addToQueueMutation.mutate([file.id])}
onClick={() => {
if (hasPermission('queue:create')) {
setScheduleFile(file);
}
}}
className={`p-1.5 rounded transition-colors ${
hasPermission('queue:create')
? 'hover:bg-bambu-dark text-bambu-gray hover:text-white'
: 'text-bambu-gray/50 cursor-not-allowed'
}`}
title={hasPermission('queue:create') ? t('fileManager.addToQueue') : t('fileManager.noPermissionAddToQueue')}
disabled={addToQueueMutation.isPending || !hasPermission('queue:create')}
title={hasPermission('queue:create') ? t('fileManager.schedulePrint') : t('fileManager.noPermissionAddToQueue')}
disabled={!hasPermission('queue:create')}
>
<Clock className="w-4 h-4" />
</button>
@@ -2236,6 +2222,22 @@ export function FileManagerPage() {
/>
)}
{scheduleFile && (
<PrintModal
mode="add-to-queue"
libraryFileId={scheduleFile.id}
archiveName={scheduleFile.print_name || scheduleFile.filename}
onClose={() => setScheduleFile(null)}
onSuccess={() => {
setScheduleFile(null);
setSelectedFiles([]);
queryClient.invalidateQueries({ queryKey: ['library-files'] });
queryClient.invalidateQueries({ queryKey: ['queue'] });
queryClient.invalidateQueries({ queryKey: ['archives'] });
}}
/>
)}
{renameItem && (
<RenameModal
type={renameItem.type}
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -23,7 +23,7 @@
<!-- Splash screens for iOS -->
<link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
<script type="module" crossorigin src="/assets/index-ChuiDDfR.js"></script>
<script type="module" crossorigin src="/assets/index-D3RdYLqK.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-Mcnrrd7v.css">
</head>
<body>