From b6c304d4a958f7b8c595f224755999b9e24cfe6e Mon Sep 17 00:00:00 2001 From: Matteo Parenti Date: Sat, 14 Feb 2026 13:52:45 +0100 Subject: [PATCH 1/3] Fixed mobile view --- frontend/src/components/Dashboard.tsx | 43 ++++++++++++++++++++-- frontend/src/components/FilamentTrends.tsx | 30 +++++++++------ frontend/src/index.css | 16 ++++++++ frontend/src/pages/MaintenancePage.tsx | 6 +-- frontend/src/pages/PrintersPage.tsx | 40 +++++++++++--------- frontend/src/pages/ProfilesPage.tsx | 18 ++++----- frontend/src/pages/StatsPage.tsx | 1 + 7 files changed, 108 insertions(+), 46 deletions(-) diff --git a/frontend/src/components/Dashboard.tsx b/frontend/src/components/Dashboard.tsx index c3b7ac2e4..d6635327d 100644 --- a/frontend/src/components/Dashboard.tsx +++ b/frontend/src/components/Dashboard.tsx @@ -32,6 +32,7 @@ interface DashboardProps { widgets: DashboardWidget[]; storageKey: string; columns?: number; + stackBelow?: number; hideControls?: boolean; onResetLayout?: () => void; renderControls?: (controls: { @@ -54,6 +55,7 @@ function SortableWidget({ component, isHidden, size, + columnSpan, onToggleVisibility, onToggleSize, }: { @@ -62,6 +64,7 @@ function SortableWidget({ component: ReactNode | ((size: 1 | 2 | 4) => ReactNode); isHidden: boolean; size: 1 | 2 | 4; + columnSpan: number; onToggleVisibility: () => void; onToggleSize: () => void; }) { @@ -87,7 +90,7 @@ function SortableWidget({ ref={setNodeRef} style={{ ...style, - gridColumn: `span ${size}`, + gridColumn: `span ${columnSpan}`, }} className={`bg-bambu-dark-secondary rounded-xl border border-bambu-dark-tertiary overflow-hidden ${ isDragging ? 'ring-2 ring-bambu-green shadow-lg' : '' @@ -135,7 +138,7 @@ function SortableWidget({ ); } -export function Dashboard({ widgets, storageKey, columns = 4, hideControls = false, onResetLayout, renderControls }: DashboardProps) { +export function Dashboard({ widgets, storageKey, columns = 4, stackBelow, hideControls = false, onResetLayout, renderControls }: DashboardProps) { // Build default sizes from widget definitions const getDefaultSizes = () => { const sizes: Record = {}; @@ -169,6 +172,31 @@ export function Dashboard({ widgets, storageKey, columns = 4, hideControls = fal }); const [showHiddenPanel, setShowHiddenPanel] = useState(false); + const [isStacked, setIsStacked] = useState(false); + + useEffect(() => { + if (!stackBelow) return undefined; + const mediaQuery = window.matchMedia(`(max-width: ${stackBelow}px)`); + const handleChange = (event: MediaQueryListEvent | MediaQueryList) => { + setIsStacked('matches' in event ? event.matches : event.matches); + }; + handleChange(mediaQuery); + const onChange = (event: MediaQueryListEvent) => handleChange(event); + if (mediaQuery.addEventListener) { + mediaQuery.addEventListener('change', onChange); + } else { + mediaQuery.addListener(onChange); + } + return () => { + if (mediaQuery.removeEventListener) { + mediaQuery.removeEventListener('change', onChange); + } else { + mediaQuery.removeListener(onChange); + } + }; + }, [stackBelow]); + + const effectiveColumns = stackBelow && isStacked ? 1 : columns; // Listen for toggle-hidden-panel event from parent useEffect(() => { @@ -324,20 +352,27 @@ export function Dashboard({ widgets, storageKey, columns = 4, hideControls = fal
{visibleWidgets.map((widget) => ( + (() => { + const size = layout.sizes[widget.id] || 2; + const columnSpan = Math.min(size, effectiveColumns); + return ( toggleVisibility(widget.id)} onToggleSize={() => toggleSize(widget.id)} /> + ); + })() ))}
diff --git a/frontend/src/components/FilamentTrends.tsx b/frontend/src/components/FilamentTrends.tsx index 05fed434c..0858fa241 100644 --- a/frontend/src/components/FilamentTrends.tsx +++ b/frontend/src/components/FilamentTrends.tsx @@ -157,7 +157,7 @@ export function FilamentTrends({ archives, currency = '$' }: FilamentTrendsProps return (
{/* Time Range Selector */} -
+

Filament Usage Trends

{(['7d', '30d', '90d', '365d', 'all'] as TimeRange[]).map((range) => ( @@ -177,24 +177,30 @@ export function FilamentTrends({ archives, currency = '$' }: FilamentTrendsProps
{/* Summary Cards */} -
+
-

Period Filament

-

{(totalFilament / 1000).toFixed(2)}kg

+
+

Period Filament

+

{(totalFilament / 1000).toFixed(2)}kg

+

{totalFilament.toFixed(0)}g total

-

Period Cost

-

{currency}{totalCost.toFixed(2)}

+
+

Period Cost

+

{currency}{totalCost.toFixed(2)}

+

{totalPrints} prints

-

Avg per Print

-

- {totalPrints > 0 - ? (totalFilament / totalPrints).toFixed(0) - : 0}g -

+
+

Avg per Print

+

+ {totalPrints > 0 + ? (totalFilament / totalPrints).toFixed(0) + : 0}g +

+

{currency}{totalPrints > 0 ? (totalCost / totalPrints).toFixed(2) : '0.00'} avg

diff --git a/frontend/src/index.css b/frontend/src/index.css index 48dbcec07..6ddd5ffe7 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -173,6 +173,22 @@ --border-color: #2a3d30; } +/* Printer card control buttons: stack only when they clip */ +.printer-control-buttons-container { + container-type: inline-size; +} + +@container (max-width: 220px) { + .printer-control-buttons { + flex-direction: column; + align-items: stretch; + } + + .printer-control-buttons > button { + width: 100%; + } +} + /* ============================================ LAYER 2: STYLE EFFECTS ============================================ */ diff --git a/frontend/src/pages/MaintenancePage.tsx b/frontend/src/pages/MaintenancePage.tsx index 248ca56db..c19a45e02 100644 --- a/frontend/src/pages/MaintenancePage.tsx +++ b/frontend/src/pages/MaintenancePage.tsx @@ -286,9 +286,9 @@ function MaintenanceCard({ return (
-
+
{/* Icon with status indicator */} -
{/* Actions */} -
+
+
0 ? 'bg-bambu-green' : 'bg-gray-500'}`} /> @@ -1335,17 +1335,21 @@ function StatusSummaryBar({ printers }: { printers: Printer[] | undefined }) { {nextFinish && ( <>
-
- {t('printers.nextAvailable')}: - {nextFinish.name} -
-
+
+
+ {t('printers.nextAvailable')}: + {nextFinish.name} +
+
+
+
+
+ {Math.round(nextFinish.progress)}% + ({formatTime(nextFinish.remainingMin * 60)})
- {Math.round(nextFinish.progress)}% - ({formatTime(nextFinish.remainingMin * 60)})
)} @@ -2575,9 +2579,9 @@ function PrinterCard({
-
+
{/* Left: Fan Status - always visible, dynamic coloring */} -
+
{/* Part Cooling Fan */}
0 ? 'bg-cyan-500/10' : 'bg-bambu-dark'}`} @@ -2613,7 +2617,7 @@ function PrinterCard({
{/* Right: Print Control Buttons */} -
+
{/* Stop button */}
{(ams.humidity != null || ams.temp != null) && ( -
+
{ams.humidity != null && ( {/* Row 2: Slot (left) + Stats (right stacked) */} -
+
{/* Slot wrapper with menu button, dropdown, and loading overlay */} -
+
{/* Loading overlay during RFID re-read */} {isHtRefreshing && (
@@ -3171,7 +3175,7 @@ function PrinterCard({
{/* Stats stacked vertically: Temp on top, Humidity below */} {(ams.humidity != null || ams.temp != null) && ( -
+
{ams.temp != null && ( void; t: TFunction }) {
)} -
+
{step === 'code' && (