Bladeren bron

Fix status indicators using accent color instead of semantic colors

Status badges (Connected/Offline, HMS errors, maintenance, success/failed)
were using bambu-green which follows the user's accent color theme. This
caused confusing UX - e.g., "Connected" appearing red with a red accent.

Added fixed semantic status colors that never change with theme:
- status-ok: #22c55e (green) - success, online, OK states
- status-error: #ef4444 (red) - failed, offline, error states
- status-warning: #f59e0b (amber) - warnings, pending states

Updated components:
- PrintersPage: connection badge, HMS errors, maintenance, WiFi signal
- SmartPlugCard/SwitchbarPopover: plug connectivity status
- StatsPage: success/failed counts, failure rate, time accuracy
- QueuePage: queue item statuses (pending, completed, failed)
- ArchivesPage: failed/aborted archive badge
- ProjectDetailPage: child status, timeline events, BOM checklist
- CompareArchivesModal: archive completion status
- NotificationProviderCard: last success/error status

Closes #136
maziggy 4 maanden geleden
bovenliggende
commit
bda659e11d

+ 2 - 2
frontend/src/components/CompareArchivesModal.tsx

@@ -91,8 +91,8 @@ function ComparisonContent({ comparison }: { comparison: ArchiveComparison }) {
                     {archive.print_name}
                   </div>
                   <div className={`text-xs ${
-                    archive.status === 'completed' ? 'text-bambu-green' :
-                    archive.status === 'failed' ? 'text-red-400' : 'text-bambu-gray'
+                    archive.status === 'completed' ? 'text-status-ok' :
+                    archive.status === 'failed' ? 'text-status-error' : 'text-bambu-gray'
                   }`}>
                     {archive.status}
                   </div>

+ 2 - 2
frontend/src/components/NotificationProviderCard.tsx

@@ -91,13 +91,13 @@ export function NotificationProviderCard({ provider, onEdit }: NotificationProvi
             {/* Quick enable/disable toggle + Status indicator */}
             <div className="flex items-center gap-3">
               {provider.last_success && (
-                <span className="text-xs text-bambu-green hidden sm:inline">Last: {formatDateOnly(provider.last_success)}</span>
+                <span className="text-xs text-status-ok hidden sm:inline">Last: {formatDateOnly(provider.last_success)}</span>
               )}
               {/* Only show error if it's more recent than last success */}
               {provider.last_error && provider.last_error_at && (
                 !provider.last_success || (parseUTCDate(provider.last_error_at)?.getTime() || 0) > (parseUTCDate(provider.last_success)?.getTime() || 0)
               ) && (
-                <span className="text-xs text-red-400" title={provider.last_error}>Error</span>
+                <span className="text-xs text-status-error" title={provider.last_error}>Error</span>
               )}
               <Toggle
                 checked={provider.enabled}

+ 3 - 3
frontend/src/components/SmartPlugCard.tsx

@@ -136,11 +136,11 @@ export function SmartPlugCard({ plug, onEdit }: SmartPlugCardProps) {
                 <Loader2 className="w-4 h-4 text-bambu-gray animate-spin" />
               ) : isReachable ? (
                 <div className="flex items-center gap-1 text-sm">
-                  <Wifi className="w-4 h-4 text-bambu-green" />
-                  <span className={isOn ? 'text-bambu-green' : 'text-bambu-gray'}>{status?.state || 'Unknown'}</span>
+                  <Wifi className="w-4 h-4 text-status-ok" />
+                  <span className={isOn ? 'text-status-ok' : 'text-bambu-gray'}>{status?.state || 'Unknown'}</span>
                 </div>
               ) : (
-                <div className="flex items-center gap-1 text-sm text-red-400">
+                <div className="flex items-center gap-1 text-sm text-status-error">
                   <WifiOff className="w-4 h-4" />
                   <span>Offline</span>
                 </div>

+ 4 - 4
frontend/src/components/SwitchbarPopover.tsx

@@ -53,8 +53,8 @@ function SwitchItem({ plug }: { plug: SmartPlug }) {
                 <Loader2 className="w-3 h-3 text-bambu-gray animate-spin" />
               ) : isReachable ? (
                 <>
-                  <Wifi className="w-3 h-3 text-bambu-green" />
-                  <span className={isOn ? 'text-bambu-green' : 'text-bambu-gray'}>{status?.state || 'Unknown'}</span>
+                  <Wifi className="w-3 h-3 text-status-ok" />
+                  <span className={isOn ? 'text-status-ok' : 'text-bambu-gray'}>{status?.state || 'Unknown'}</span>
                   {status?.energy?.power !== null && status?.energy?.power !== undefined && (
                     <>
                       <span className="text-bambu-gray mx-1">|</span>
@@ -65,8 +65,8 @@ function SwitchItem({ plug }: { plug: SmartPlug }) {
                 </>
               ) : (
                 <>
-                  <WifiOff className="w-3 h-3 text-red-400" />
-                  <span className="text-red-400">Offline</span>
+                  <WifiOff className="w-3 h-3 text-status-error" />
+                  <span className="text-status-error">Offline</span>
                 </>
               )}
             </div>

+ 10 - 0
frontend/src/index.css

@@ -10,6 +10,11 @@
   --color-bambu-green-light: var(--accent-light);
   --color-bambu-green-dark: var(--accent-dark);
 
+  /* Semantic status colors - fixed, don't change with accent */
+  --color-status-ok: var(--status-ok);
+  --color-status-error: var(--status-error);
+  --color-status-warning: var(--status-warning);
+
   /* Theme-aware colors via CSS variables */
   --color-bambu-dark: var(--bg-primary);
   --color-bambu-dark-secondary: var(--bg-secondary);
@@ -29,6 +34,11 @@
   --accent-light: #00c64d;
   --accent-dark: #009438;
 
+  /* Semantic status colors - these never change with accent theme */
+  --status-ok: #22c55e;      /* green-500 - always green for success/online/ok */
+  --status-error: #ef4444;   /* red-500 - always red for error/offline/failed */
+  --status-warning: #f59e0b; /* amber-500 - always amber for warnings */
+
   /* Default light mode background (neutral) */
   --bg-primary: #f5f5f5;
   --bg-secondary: #ffffff;

+ 1 - 1
frontend/src/pages/ArchivesPage.tsx

@@ -543,7 +543,7 @@ function ArchiveCard({
           />
         </button>
         {(archive.status === 'failed' || archive.status === 'aborted') && (
-          <div className="absolute top-2 left-12 px-2 py-1 rounded text-xs bg-red-500/80 text-white">
+          <div className="absolute top-2 left-12 px-2 py-1 rounded text-xs bg-status-error/80 text-white">
             {archive.status === 'aborted' ? 'cancelled' : 'failed'}
           </div>
         )}

+ 15 - 15
frontend/src/pages/PrintersPage.tsx

@@ -1354,7 +1354,7 @@ function PrinterCard({
                   {viewMode === 'compact' && (
                     <div
                       className={`w-2 h-2 rounded-full flex-shrink-0 ${
-                        status?.connected ? 'bg-bambu-green' : 'bg-red-500'
+                        status?.connected ? 'bg-status-ok' : 'bg-status-error'
                       }`}
                       title={status?.connected ? 'Connected' : 'Offline'}
                     />
@@ -1440,8 +1440,8 @@ function PrinterCard({
               <span
                 className={`flex items-center gap-1.5 px-2 py-1 rounded-full text-xs ${
                   status?.connected
-                    ? 'bg-bambu-green/20 text-bambu-green'
-                    : 'bg-red-500/20 text-red-400'
+                    ? 'bg-status-ok/20 text-status-ok'
+                    : 'bg-status-error/20 text-status-error'
                 }`}
               >
                 {status?.connected ? (
@@ -1456,14 +1456,14 @@ function PrinterCard({
                 <span
                   className={`flex items-center gap-1 px-2 py-1 rounded-full text-xs ${
                     wifiSignal >= -50
-                      ? 'bg-bambu-green/20 text-bambu-green'
+                      ? 'bg-status-ok/20 text-status-ok'
                       : wifiSignal >= -60
-                      ? 'bg-bambu-green/20 text-bambu-green'
+                      ? 'bg-status-ok/20 text-status-ok'
                       : wifiSignal >= -70
-                      ? 'bg-amber-500/20 text-amber-600'
+                      ? 'bg-status-warning/20 text-status-warning'
                       : wifiSignal >= -80
                       ? 'bg-orange-500/20 text-orange-600'
-                      : 'bg-red-500/20 text-red-600'
+                      : 'bg-status-error/20 text-status-error'
                   }`}
                   title={`WiFi: ${wifiSignal} dBm - ${getWifiStrength(wifiSignal).label}`}
                 >
@@ -1480,9 +1480,9 @@ function PrinterCard({
                     className={`flex items-center gap-1 px-2 py-1 rounded-full text-xs cursor-pointer hover:opacity-80 transition-opacity ${
                       knownErrors.length > 0
                         ? knownErrors.some(e => e.severity <= 2)
-                          ? 'bg-red-500/20 text-red-400'
-                          : 'bg-orange-500/20 text-orange-400'
-                        : 'bg-bambu-green/20 text-bambu-green'
+                          ? 'bg-status-error/20 text-status-error'
+                          : 'bg-status-warning/20 text-status-warning'
+                        : 'bg-status-ok/20 text-status-ok'
                     }`}
                     title="Click to view HMS errors"
                   >
@@ -1497,10 +1497,10 @@ function PrinterCard({
                   onClick={() => navigate('/maintenance')}
                   className={`flex items-center gap-1 px-2 py-1 rounded-full text-xs cursor-pointer hover:opacity-80 transition-opacity ${
                     maintenanceInfo.due_count > 0
-                      ? 'bg-red-500/20 text-red-400'
+                      ? 'bg-status-error/20 text-status-error'
                       : maintenanceInfo.warning_count > 0
-                      ? 'bg-orange-500/20 text-orange-400'
-                      : 'bg-bambu-green/20 text-bambu-green'
+                      ? 'bg-status-warning/20 text-status-warning'
+                      : 'bg-status-ok/20 text-status-ok'
                   }`}
                   title={
                     maintenanceInfo.due_count > 0 || maintenanceInfo.warning_count > 0
@@ -3487,8 +3487,8 @@ function FirmwareUpdateModal({
               <div className="w-full bg-bambu-dark-tertiary rounded-full h-2">
                 <div
                   className={`h-2 rounded-full transition-all ${
-                    uploadStatus.status === 'error' ? 'bg-red-500' :
-                    uploadStatus.status === 'complete' ? 'bg-bambu-green' : 'bg-orange-500'
+                    uploadStatus.status === 'error' ? 'bg-status-error' :
+                    uploadStatus.status === 'complete' ? 'bg-status-ok' : 'bg-orange-500'
                   } ${uploadStatus.status === 'uploading' ? 'animate-pulse' : ''}`}
                   style={{ width: `${uploadStatus.progress}%` }}
                 />

+ 6 - 6
frontend/src/pages/ProjectDetailPage.tsx

@@ -520,7 +520,7 @@ export function ProjectDetailPage() {
                   <p className="text-sm text-bambu-gray">Print Jobs</p>
                   <p className="text-xl font-semibold text-white">{stats.total_archives} <span className="text-sm font-normal text-bambu-gray">total</span></p>
                   {stats.failed_prints > 0 && (
-                    <p className="text-sm text-red-400">{stats.failed_prints} failed</p>
+                    <p className="text-sm text-status-error">{stats.failed_prints} failed</p>
                   )}
                   <p className="text-sm text-bambu-gray">{stats.completed_prints} parts printed</p>
                 </div>
@@ -610,7 +610,7 @@ export function ProjectDetailPage() {
                     />
                     <span className="text-white">{child.name}</span>
                     <span className={`text-xs px-2 py-0.5 rounded ${
-                      child.status === 'completed' ? 'bg-bambu-green/20 text-bambu-green' :
+                      child.status === 'completed' ? 'bg-status-ok/20 text-status-ok' :
                       child.status === 'archived' ? 'bg-bambu-gray/20 text-bambu-gray' :
                       'bg-blue-500/20 text-blue-400'
                     }`}>
@@ -900,7 +900,7 @@ export function ProjectDetailPage() {
                 <div
                   key={item.id}
                   className={`p-3 rounded-lg transition-colors ${
-                    item.is_complete ? 'bg-bambu-green/10' : 'bg-bambu-dark'
+                    item.is_complete ? 'bg-status-ok/10' : 'bg-bambu-dark'
                   }`}
                 >
                   <div className="flex items-start gap-3">
@@ -909,7 +909,7 @@ export function ProjectDetailPage() {
                       disabled={updateBomMutation.isPending}
                       className={`w-5 h-5 mt-0.5 rounded border-2 flex items-center justify-center transition-colors flex-shrink-0 ${
                         item.is_complete
-                          ? 'bg-bambu-green border-bambu-green text-white'
+                          ? 'bg-status-ok border-status-ok text-white'
                           : 'border-bambu-gray hover:border-bambu-green'
                       }`}
                     >
@@ -1006,8 +1006,8 @@ export function ProjectDetailPage() {
               {timeline.slice(0, 10).map((event, index) => (
                 <div key={index} className="flex gap-3">
                   <div className={`w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0 ${
-                    event.event_type === 'print_completed' ? 'bg-bambu-green/20 text-bambu-green' :
-                    event.event_type === 'print_failed' ? 'bg-red-500/20 text-red-400' :
+                    event.event_type === 'print_completed' ? 'bg-status-ok/20 text-status-ok' :
+                    event.event_type === 'print_failed' ? 'bg-status-error/20 text-status-error' :
                     event.event_type === 'print_started' ? 'bg-yellow-500/20 text-yellow-400' :
                     'bg-bambu-dark-tertiary text-bambu-gray'
                   }`}>

+ 3 - 3
frontend/src/pages/QueuePage.tsx

@@ -76,10 +76,10 @@ function formatRelativeTime(dateString: string | null, timeFormat: TimeFormat =
 
 function StatusBadge({ status }: { status: PrintQueueItem['status'] }) {
   const config = {
-    pending: { icon: Clock, color: 'text-yellow-400 bg-yellow-400/10 border-yellow-400/20', label: 'Pending' },
+    pending: { icon: Clock, color: 'text-status-warning bg-status-warning/10 border-status-warning/20', label: 'Pending' },
     printing: { icon: Play, color: 'text-blue-400 bg-blue-400/10 border-blue-400/20', label: 'Printing' },
-    completed: { icon: CheckCircle, color: 'text-green-400 bg-green-400/10 border-green-400/20', label: 'Completed' },
-    failed: { icon: XCircle, color: 'text-red-400 bg-red-400/10 border-red-400/20', label: 'Failed' },
+    completed: { icon: CheckCircle, color: 'text-status-ok bg-status-ok/10 border-status-ok/20', label: 'Completed' },
+    failed: { icon: XCircle, color: 'text-status-error bg-status-error/10 border-status-error/20', label: 'Failed' },
     skipped: { icon: SkipForward, color: 'text-orange-400 bg-orange-400/10 border-orange-400/20', label: 'Skipped' },
     cancelled: { icon: X, color: 'text-gray-400 bg-gray-400/10 border-gray-400/20', label: 'Cancelled' },
   };

+ 7 - 7
frontend/src/pages/StatsPage.tsx

@@ -155,12 +155,12 @@ function SuccessRateWidget({
       <div className="flex-1 min-w-0">
         <div className="space-y-2">
           <div className="flex items-center gap-2">
-            <CheckCircle className="w-4 h-4 text-bambu-green flex-shrink-0" />
+            <CheckCircle className="w-4 h-4 text-status-ok flex-shrink-0" />
             <span className="text-sm text-bambu-gray">Successful:</span>
             <span className="text-sm text-white font-medium">{stats?.successful_prints || 0}</span>
           </div>
           <div className="flex items-center gap-2">
-            <XCircle className="w-4 h-4 text-red-400 flex-shrink-0" />
+            <XCircle className="w-4 h-4 text-status-error flex-shrink-0" />
             <span className="text-sm text-bambu-gray">Failed:</span>
             <span className="text-sm text-white font-medium">{stats?.failed_prints || 0}</span>
           </div>
@@ -276,8 +276,8 @@ function TimeAccuracyWidget({
                   {printerMap.get(printerId) || `Printer ${printerId}`}
                 </span>
                 <span className={`font-medium ${
-                  acc >= 95 && acc <= 105 ? 'text-bambu-green' :
-                  acc > 105 ? 'text-blue-400' : 'text-orange-400'
+                  acc >= 95 && acc <= 105 ? 'text-status-ok' :
+                  acc > 105 ? 'text-blue-400' : 'text-status-warning'
                 }`}>
                   {acc.toFixed(0)}%
                 </span>
@@ -452,7 +452,7 @@ function FailureAnalysisWidget({ size = 1 }: { size?: 1 | 2 | 4 }) {
       <div className={size >= 2 ? 'flex-shrink-0' : ''}>
         <div className="flex items-center gap-4">
           <div className="flex items-center gap-2">
-            <AlertTriangle className={`w-5 h-5 ${analysis.failure_rate > 20 ? 'text-red-400' : analysis.failure_rate > 10 ? 'text-yellow-400' : 'text-bambu-green'}`} />
+            <AlertTriangle className={`w-5 h-5 ${analysis.failure_rate > 20 ? 'text-status-error' : analysis.failure_rate > 10 ? 'text-status-warning' : 'text-status-ok'}`} />
             <span className={`font-bold text-white ${size >= 2 ? 'text-3xl' : 'text-2xl'}`}>{analysis.failure_rate.toFixed(1)}%</span>
           </div>
         </div>
@@ -465,8 +465,8 @@ function FailureAnalysisWidget({ size = 1 }: { size?: 1 | 2 | 4 }) {
             <div className="flex items-center gap-2 text-sm">
               <TrendingDown className={`w-4 h-4 ${
                 analysis.trend[analysis.trend.length - 1].failure_rate < analysis.trend[analysis.trend.length - 2].failure_rate
-                  ? 'text-bambu-green'
-                  : 'text-red-400'
+                  ? 'text-status-ok'
+                  : 'text-status-error'
               }`} />
               <span className="text-bambu-gray">
                 Last week: {analysis.trend[analysis.trend.length - 1].failure_rate.toFixed(1)}%

File diff suppressed because it is too large
+ 0 - 0
static/assets/index-CVDQtTMh.css


File diff suppressed because it is too large
+ 0 - 0
static/assets/index-DVKqcow3.css


File diff suppressed because it is too large
+ 0 - 0
static/assets/index-oIN8PllK.js


+ 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-SsFUChPG.js"></script>
-    <link rel="stylesheet" crossorigin href="/assets/index-CVDQtTMh.css">
+    <script type="module" crossorigin src="/assets/index-oIN8PllK.js"></script>
+    <link rel="stylesheet" crossorigin href="/assets/index-DVKqcow3.css">
   </head>
   <body>
     <div id="root"></div>

Some files were not shown because too many files changed in this diff