From 6c308ecbd21904bc9431a5f9a9e3f996b68ac585 Mon Sep 17 00:00:00 2001 From: AneoPsy Date: Wed, 18 Feb 2026 01:50:27 -1000 Subject: [PATCH] feat(queue): improve mobile responsiveness for QueuePage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Summary cards: 2-col mobile grid with responsive padding, icons, and text sizing; 5th card spans full width on 2-col layout - Filters: flex-wrap with scaled text/padding; selects share width on mobile; clear history button goes full-width - Section headers (Pending/History): flex-wrap so sort controls wrap below title; drag hint hidden on mobile; responsive icon and text sizes - Bulk action toolbar: flex-wrap with tighter gaps/padding; vertical divider hidden on mobile; scaled button text and icons - SortableQueueItem: top-aligned on mobile; smaller thumbnail (10→14 at sm); metadata row wraps with truncated printer name; username hidden on mobile; badges scale down; status badge and actions stack vertically on mobile; tighter button padding --- frontend/src/pages/QueuePage.tsx | 371 ++++++++++++++++--------------- 1 file changed, 196 insertions(+), 175 deletions(-) diff --git a/frontend/src/pages/QueuePage.tsx b/frontend/src/pages/QueuePage.tsx index b76317d32..eeee57751 100644 --- a/frontend/src/pages/QueuePage.tsx +++ b/frontend/src/pages/QueuePage.tsx @@ -361,26 +361,40 @@ function SortableQueueItem({ const isPending = item.status === 'pending'; const isHistory = ['completed', 'failed', 'skipped', 'cancelled'].includes(item.status); + const isMobileSelectable = isPending && onToggleSelect; + return (
{ + if (window.innerWidth < 640) onToggleSelect(); + } : undefined} > -
- {/* Selection checkbox for pending items */} + {/* Mobile selected left accent bar */} + {isMobileSelectable && isSelected && ( +
+ )} + +
+ {/* Mobile selection indicator — left accent bar only, no tick */} + + {/* Selection checkbox for pending items - hidden on mobile, tap card instead */} {isPending && onToggleSelect && ( )} - {/* Drag handle or position number */} + {/* Drag handle or position number - hidden on mobile */} {isPending ? (
- +
) : position !== undefined ? ( -
+
#{position}
) : ( -
+
)} {/* Thumbnail - use plate-specific thumbnail if plate_id is set */} -
+
{item.archive_thumbnail ? ( ) : (
- +
)}
@@ -439,7 +453,7 @@ function SortableQueueItem({ {/* Info */}
-

+

{item.archive_name || item.library_file_name || `File #${item.archive_id || item.library_file_id}`} {(platesData?.is_multi_plate ?? false) && item.plate_id !== undefined && item.plate_id !== null && ` • ${plates.find(plate => plate.index === item.plate_id)?.name || t('queue.plateNumber', { index: item.plate_id })}`}

@@ -462,57 +476,59 @@ function SortableQueueItem({ ) : null}
-
- - +
+ + + {item.target_model ? `${t('queue.filter.any')} ${item.target_model}${item.target_location ? ` @ ${item.target_location}` : ''}${item.required_filament_types?.length ? ` (${item.required_filament_types.join(', ')})` : ''}` : item.printer_id === null ? t('queue.filter.unassigned') : (item.printer_name || `${t('common.printer')} #${item.printer_id}`)} + {item.print_time_seconds && ( - - + + {formatDuration(item.print_time_seconds)} )} {item.filament_used_grams && ( - - + + {formatWeight(item.filament_used_grams)} )} {item.created_by_username && ( - + {item.created_by_username} )} {isPending && !item.manual_start && ( - - + + {formatRelativeTime(item.scheduled_time, timeFormat, t)} )}
{/* Options badges */} -
+
{item.manual_start && ( - - + + {t('queue.badges.staged')} )} {item.require_previous_success && ( - + {t('queue.badges.requiresPrevious')} )} {item.auto_off_after && ( - - + + {t('queue.badges.autoPowerOff')} )} @@ -520,17 +536,17 @@ function SortableQueueItem({ {/* Progress bar for printing items - TODO: integrate with WebSocket */} {isPrinting && status && ( -
-
-
+
+
+
{Math.round(status.progress || 0)}%
-
+
{status.remaining_time != null && status.remaining_time > 0 && ( <> @@ -554,7 +570,7 @@ function SortableQueueItem({ {/* Waiting reason for model-based assignments */} {item.waiting_reason && item.status === 'pending' && ( -

+

{item.waiting_reason}

@@ -562,88 +578,92 @@ function SortableQueueItem({ {/* Error message */} {item.error_message && ( -

+

{item.error_message}

)}
- {/* Status badge */} - + {/* Status badge + Actions */} + {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */} +
e.stopPropagation()}> + - {/* Actions */} -
- {isPrinting && ( - - )} - {isPending && ( - <> - {item.manual_start && ( +
+ {isPrinting && ( + + )} + {isPending && ( + <> + {item.manual_start && ( + + )} - )} - - - - )} - {isHistory && ( - <> - - - - )} + + + )} + {isHistory && ( + <> + + + + )} +
@@ -987,72 +1007,72 @@ export function QueuePage() {
{/* Summary Cards */} -
+
- -
-
- + +
+
+
-
-

{activeItems.length}

-

{t('queue.summary.printing')}

+
+

{activeItems.length}

+

{t('queue.summary.printing')}

- -
-
- + +
+
+
-
-

{pendingItems.length}

-

{t('queue.summary.queued')}

+
+

{pendingItems.length}

+

{t('queue.summary.queued')}

- -
-
- + +
+
+
-
-

{formatDuration(totalQueueTime)}

-

{t('queue.summary.totalTime')}

+
+

{formatDuration(totalQueueTime)}

+

{t('queue.summary.totalTime')}

- -
-
- + +
+
+
-
-

{formatWeight(totalWeight)}

-

{t('queue.summary.totalWeight')}

+
+

{formatWeight(totalWeight)}

+

{t('queue.summary.totalWeight')}

- - -
-
- + + +
+
+
-
-

{historyItems.length}

-

{t('queue.summary.history')}

+
+

{historyItems.length}

+

{t('queue.summary.history')}

@@ -1060,9 +1080,9 @@ export function QueuePage() {
{/* Filters */} -
+
setFilterLocation(e.target.value)} > @@ -1105,10 +1125,11 @@ export function QueuePage() { )} -
+
{historyItems.length > 0 && (
-
+
{historyItems.slice(0, 20).map((item, index) => (