Browse Source

Merge pull request #1272 from EdwardChamberlain/feat-page-icons

[Feature] Update page icons for consistent UI style
Ed 2 weeks ago
parent
commit
14a6d294f4

+ 1 - 1
frontend/src/__tests__/pages/StatsPage.test.tsx

@@ -164,7 +164,7 @@ describe('StatsPage', () => {
       render(<StatsPage />);
 
       await waitFor(() => {
-        expect(screen.getByText('Dashboard')).toBeInTheDocument();
+        expect(screen.getByText('Statistics')).toBeInTheDocument();
       });
     });
 

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

@@ -1,6 +1,6 @@
 import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
 import { NavLink, Outlet, useNavigate, useLocation } from 'react-router-dom';
-import { Printer, Archive, Calendar, BarChart3, Cloud, Settings, Sun, Moon, ChevronLeft, ChevronRight, Keyboard, Github, GripVertical, ArrowUpCircle, Wrench, FolderKanban, FolderOpen, X, Menu, Info, Plug, Bug, LogOut, Key, Loader2, Disc3, ShieldAlert, Bell, Globe, type LucideIcon } from 'lucide-react';
+import { Printer, Archive, ListOrdered, BarChart3, Cloud, Settings, Sun, Moon, ChevronLeft, ChevronRight, Keyboard, Github, GripVertical, ArrowUpCircle, Wrench, FolderKanban, FolderOpen, X, Menu, Info, Plug, Bug, LogOut, Key, Loader2, Disc3, ShieldAlert, Bell, Globe, type LucideIcon } from 'lucide-react';
 import { useTranslation } from 'react-i18next';
 import { useTheme } from '../contexts/ThemeContext';
 import { KeyboardShortcutsModal } from './KeyboardShortcutsModal';
@@ -29,7 +29,7 @@ export const defaultNavItems: NavItem[] = [
   { id: 'printers', to: '/', icon: Printer, labelKey: 'nav.printers' },
   { id: 'inventory', to: '/inventory', icon: Disc3, labelKey: 'nav.inventory' },
   { id: 'archives', to: '/archives', icon: Archive, labelKey: 'nav.archives' },
-  { id: 'queue', to: '/queue', icon: Calendar, labelKey: 'nav.queue' },
+  { id: 'queue', to: '/queue', icon: ListOrdered, labelKey: 'nav.queue' },
   { id: 'projects', to: '/projects', icon: FolderKanban, labelKey: 'nav.projects' },
   { id: 'files', to: '/files', icon: FolderOpen, labelKey: 'nav.files' },
   { id: 'makerworld', to: '/makerworld', icon: Globe, labelKey: 'nav.makerworld' },

+ 3 - 2
frontend/src/i18n/locales/de.ts

@@ -3,7 +3,7 @@ export default {
   nav: {
     printers: 'Drucker',
     archives: 'Archiv',
-    queue: 'Warteschlange',
+    queue: 'Druckwarteschlange',
     stats: 'Statistiken',
     profiles: 'Profile',
     maintenance: 'Wartung',
@@ -1141,7 +1141,7 @@ export default {
 
   // Statistics page
   stats: {
-    title: 'Dashboard',
+    title: 'Statistiken',
     subtitle: 'Widgets zum Neuanordnen ziehen. Auf das Augensymbol klicken zum Ausblenden.',
     overview: 'Übersicht',
     totalPrints: 'Gesamtdrucke',
@@ -3420,6 +3420,7 @@ export default {
   // Inventar
   inventory: {
     title: 'Spulen-Inventar',
+    subtitle: 'Verwalten Sie Ihre Spulen',
     spoolmanMixedContentTitle: 'Spoolman lässt sich nicht über HTTPS laden — Browser blockiert gemischte Inhalte',
     spoolmanMixedContentBody: 'Bambuddy wird über HTTPS ausgeliefert (über deinen Reverse-Proxy), aber deine Spoolman-URL ist nach wie vor HTTP. Browser blockieren gemischte Inhalte aus Sicherheitsgründen, daher kann die eingebettete Spoolman-Oberfläche nicht geladen werden. Spoolman muss ebenfalls über HTTPS erreichbar sein.',
     spoolmanMixedContentFixReverseProxy: 'Stelle Spoolman hinter denselben Reverse-Proxy wie Bambuddy (Traefik / Nginx / Caddy) mit HTTPS und aktualisiere die Spoolman-URL in den Einstellungen auf die neue HTTPS-Adresse.',

+ 3 - 2
frontend/src/i18n/locales/en.ts

@@ -3,7 +3,7 @@ export default {
   nav: {
     printers: 'Printers',
     archives: 'Archives',
-    queue: 'Queue',
+    queue: 'Print Queue',
     stats: 'Statistics',
     profiles: 'Profiles',
     maintenance: 'Maintenance',
@@ -1141,7 +1141,7 @@ export default {
 
   // Statistics page
   stats: {
-    title: 'Dashboard',
+    title: 'Statistics',
     subtitle: 'Drag widgets to rearrange. Click the eye icon to hide.',
     overview: 'Overview',
     totalPrints: 'Total Prints',
@@ -3423,6 +3423,7 @@ export default {
   // Inventory
   inventory: {
     title: 'Spool Inventory',
+    subtitle: 'Manage your spools',
     spoolmanMixedContentTitle: 'Spoolman can\'t load over HTTPS — mixed-content blocked by your browser',
     spoolmanMixedContentBody: 'Bambuddy is served over HTTPS (via your reverse proxy), but your Spoolman URL is still plain HTTP. Browsers block mixed content for security, so the embedded Spoolman UI can\'t render. Spoolman needs to be reachable over HTTPS for this to work.',
     spoolmanMixedContentFixReverseProxy: 'Put Spoolman behind the same reverse proxy as Bambuddy (Traefik / Nginx / Caddy) with HTTPS, then update the Spoolman URL in Settings to the new HTTPS address.',

+ 5 - 4
frontend/src/i18n/locales/fr.ts

@@ -3,7 +3,7 @@ export default {
   nav: {
     printers: 'Imprimantes',
     archives: 'Archives',
-    queue: 'File d\'attente',
+    queue: 'File d\'attente d\'impression',
     stats: 'Statistiques',
     profiles: 'Profils',
     maintenance: 'Maintenance',
@@ -186,8 +186,8 @@ export default {
       allLocations: 'Tous les emplacements',
     },
     toolbar: {
-      filters: 'Filters',
-      view: 'View',
+      filters: 'Filtres',
+      view: 'Vue',
       actions: 'Actions',
     },
     // Printer card
@@ -1141,7 +1141,7 @@ export default {
 
   // Statistics page
   stats: {
-    title: 'Tableau de bord',
+    title: 'Statistiques',
     subtitle: 'Glissez les widgets pour réorganiser. Cliquez sur l\'œil pour masquer.',
     overview: 'Vue d\'ensemble',
     totalPrints: 'Total impressions',
@@ -3409,6 +3409,7 @@ export default {
   // Inventory
   inventory: {
     title: 'Inventaire de Bobines',
+    subtitle: 'Gérez vos bobines',
     spoolmanMixedContentTitle: 'Spoolman ne peut pas se charger en HTTPS — contenu mixte bloqué par votre navigateur',
     spoolmanMixedContentBody: 'Bambuddy est servi en HTTPS (via votre reverse proxy), mais votre URL Spoolman est encore en HTTP. Les navigateurs bloquent le contenu mixte pour des raisons de sécurité, donc l\'interface Spoolman intégrée ne peut pas s\'afficher. Spoolman doit être accessible en HTTPS.',
     spoolmanMixedContentFixReverseProxy: 'Placez Spoolman derrière le même reverse proxy que Bambuddy (Traefik / Nginx / Caddy) en HTTPS, puis mettez à jour l\'URL Spoolman dans les Paramètres avec la nouvelle adresse HTTPS.',

+ 6 - 5
frontend/src/i18n/locales/it.ts

@@ -3,7 +3,7 @@ export default {
   nav: {
     printers: 'Stampanti',
     archives: 'Archivi',
-    queue: 'Coda',
+    queue: 'Coda di stampa',
     stats: 'Statistiche',
     profiles: 'Profili',
     maintenance: 'Manutenzione',
@@ -186,9 +186,9 @@ export default {
       allLocations: 'Tutti i luoghi',
     },
     toolbar: {
-      filters: 'Filters',
-      view: 'View',
-      actions: 'Actions',
+      filters: 'Filtri',
+      view: 'Vista',
+      actions: 'Azioni',
     },
     // Printer card
     readyToPrint: 'Pronta a stampare',
@@ -1141,7 +1141,7 @@ export default {
 
   // Statistics page
   stats: {
-    title: 'Dashboard',
+    title: 'Statistiche',
     subtitle: 'Trascina i widget per riordinare. Clicca l\'icona occhio per nascondere.',
     overview: 'Panoramica',
     totalPrints: 'Stampe totali',
@@ -3408,6 +3408,7 @@ export default {
   // Inventory
   inventory: {
     title: 'Inventario Bobine',
+    subtitle: 'Gestisci le tue bobine',
     spoolmanMixedContentTitle: 'Spoolman non può essere caricato tramite HTTPS — contenuto misto bloccato dal browser',
     spoolmanMixedContentBody: 'Bambuddy viene servito tramite HTTPS (dietro il tuo reverse proxy), ma l\'URL di Spoolman è ancora HTTP. I browser bloccano il contenuto misto per motivi di sicurezza, quindi l\'interfaccia Spoolman incorporata non può essere visualizzata. Anche Spoolman deve essere raggiungibile via HTTPS.',
     spoolmanMixedContentFixReverseProxy: 'Metti Spoolman dietro lo stesso reverse proxy di Bambuddy (Traefik / Nginx / Caddy) in HTTPS, poi aggiorna l\'URL di Spoolman nelle Impostazioni con il nuovo indirizzo HTTPS.',

+ 5 - 4
frontend/src/i18n/locales/ja.ts

@@ -3,7 +3,7 @@ export default {
   nav: {
     printers: 'プリンター',
     archives: 'アーカイブ',
-    queue: 'キュー',
+    queue: '印刷キュー',
     stats: '統計',
     profiles: 'プロファイル',
     maintenance: 'メンテナンス',
@@ -185,9 +185,9 @@ export default {
       allLocations: 'すべての場所',
     },
     toolbar: {
-      filters: 'Filters',
-      view: 'View',
-      actions: 'Actions',
+      filters: 'フィルター',
+      view: '表示',
+      actions: '操作',
     },
     // Printer card
     readyToPrint: '印刷可能',
@@ -3420,6 +3420,7 @@ export default {
   // Inventory
   inventory: {
     title: 'スプール在庫管理',
+    subtitle: 'スプールを管理',
     spoolmanMixedContentTitle: 'Spoolman を HTTPS で読み込めません — ブラウザが混在コンテンツをブロックしています',
     spoolmanMixedContentBody: 'Bambuddy はリバースプロキシ経由で HTTPS 配信されていますが、Spoolman の URL は HTTP のままです。ブラウザはセキュリティ上の理由で混在コンテンツをブロックするため、埋め込みの Spoolman UI を表示できません。Spoolman も HTTPS でアクセスできる必要があります。',
     spoolmanMixedContentFixReverseProxy: 'Spoolman を Bambuddy と同じリバースプロキシ(Traefik / Nginx / Caddy)の後ろに HTTPS で配置し、設定で Spoolman URL を新しい HTTPS アドレスに更新してください。',

+ 6 - 5
frontend/src/i18n/locales/pt-BR.ts

@@ -3,7 +3,7 @@ export default {
   nav: {
     printers: 'Impressoras',
     archives: 'Arquivos',
-    queue: 'Fila',
+    queue: 'Fila de impressão',
     stats: 'Estatísticas',
     profiles: 'Perfis',
     maintenance: 'Manutenção',
@@ -186,9 +186,9 @@ export default {
       allLocations: 'Todos os locais',
     },
     toolbar: {
-      filters: 'Filters',
-      view: 'View',
-      actions: 'Actions',
+      filters: 'Filtros',
+      view: 'Visualização',
+      actions: 'Ações',
     },
     // Printer card
     readyToPrint: 'Pronto para imprimir',
@@ -1141,7 +1141,7 @@ export default {
 
   // Statistics page
   stats: {
-    title: 'Dashboard',
+    title: 'Estatísticas',
     subtitle: 'Arraste os widgets para reorganizar. Clique no ícone de olho para ocultar.',
     overview: 'Visão Geral',
     totalPrints: 'Total de Impressões',
@@ -3408,6 +3408,7 @@ export default {
   // Inventory
   inventory: {
     title: 'Inventário de Carretéis',
+    subtitle: 'Gerencie seus carretéis',
     spoolmanMixedContentTitle: 'Spoolman não pode carregar em HTTPS — conteúdo misto bloqueado pelo navegador',
     spoolmanMixedContentBody: 'O Bambuddy é servido via HTTPS (pelo seu reverse proxy), mas a URL do Spoolman ainda é HTTP. Os navegadores bloqueiam conteúdo misto por segurança, então a interface embutida do Spoolman não consegue carregar. O Spoolman também precisa estar acessível via HTTPS.',
     spoolmanMixedContentFixReverseProxy: 'Coloque o Spoolman atrás do mesmo reverse proxy do Bambuddy (Traefik / Nginx / Caddy) com HTTPS e atualize a URL do Spoolman em Configurações com o novo endereço HTTPS.',

+ 6 - 5
frontend/src/i18n/locales/zh-CN.ts

@@ -3,7 +3,7 @@ export default {
   nav: {
     printers: '打印机',
     archives: '归档',
-    queue: '队列',
+    queue: '打印队列',
     stats: '统计',
     profiles: '配置文件',
     maintenance: '维护',
@@ -186,9 +186,9 @@ export default {
       allLocations: '所有位置',
     },
     toolbar: {
-      filters: 'Filters',
-      view: 'View',
-      actions: 'Actions',
+      filters: '筛选',
+      view: '视图',
+      actions: '操作',
     },
     // Printer card
     readyToPrint: '准备打印',
@@ -1141,7 +1141,7 @@ export default {
 
   // Statistics page
   stats: {
-    title: '仪表板',
+    title: '统计',
     subtitle: '拖动小部件以重新排列。点击眼睛图标隐藏。',
     overview: '概览',
     totalPrints: '总打印次数',
@@ -3408,6 +3408,7 @@ export default {
   // Inventory
   inventory: {
     title: '耗材库存',
+    subtitle: '管理您的料盘',
     spoolmanMixedContentTitle: 'Spoolman 无法通过 HTTPS 加载 — 浏览器已阻止混合内容',
     spoolmanMixedContentBody: 'Bambuddy 通过您的反向代理以 HTTPS 提供服务,但您的 Spoolman 地址仍为 HTTP。出于安全考虑,浏览器会阻止混合内容,因此嵌入式 Spoolman 界面无法加载。Spoolman 也必须通过 HTTPS 访问。',
     spoolmanMixedContentFixReverseProxy: '请将 Spoolman 置于与 Bambuddy 相同的反向代理(Traefik / Nginx / Caddy)之后并启用 HTTPS,然后在设置中将 Spoolman URL 更新为新的 HTTPS 地址。',

+ 6 - 5
frontend/src/i18n/locales/zh-TW.ts

@@ -3,7 +3,7 @@ export default {
   nav: {
     printers: '印表機',
     archives: '歸檔',
-    queue: '佇列',
+    queue: '列印佇列',
     stats: '統計',
     profiles: '設定檔案',
     maintenance: '維護',
@@ -186,9 +186,9 @@ export default {
       allLocations: '所有位置',
     },
     toolbar: {
-      filters: 'Filters',
-      view: 'View',
-      actions: 'Actions',
+      filters: '篩選',
+      view: '檢視',
+      actions: '操作',
     },
     // Printer card
     readyToPrint: '準備列印',
@@ -1141,7 +1141,7 @@ export default {
 
   // Statistics page
   stats: {
-    title: '儀表板',
+    title: '統計',
     subtitle: '拖曳小工具以重新排列。點選眼睛圖示隱藏。',
     overview: '概覽',
     totalPrints: '總列印次數',
@@ -3408,6 +3408,7 @@ export default {
   // Inventory
   inventory: {
     title: '耗材庫存',
+    subtitle: '管理您的料盤',
     spoolmanMixedContentTitle: 'Spoolman 無法透過 HTTPS 載入 — 瀏覽器已封鎖混合內容',
     spoolmanMixedContentBody: 'Bambuddy 透過您的反向代理以 HTTPS 提供服務,但您的 Spoolman 位址仍為 HTTP。基於安全考量,瀏覽器會封鎖混合內容,因此內嵌的 Spoolman 介面無法載入。Spoolman 也必須可透過 HTTPS 存取。',
     spoolmanMixedContentFixReverseProxy: '請將 Spoolman 置於與 Bambuddy 相同的反向代理(Traefik / Nginx / Caddy)之後並啟用 HTTPS,然後在設定中將 Spoolman URL 更新為新的 HTTPS 位址。',

+ 12 - 6
frontend/src/pages/ArchivesPage.tsx

@@ -54,6 +54,7 @@ import {
   ClipboardList,
   Zap,
   Cog,
+  Archive as ArchiveIcon,
 } from 'lucide-react';
 import { api } from '../api/client';
 import { SliceModal } from '../components/SliceModal';
@@ -3076,10 +3077,18 @@ export function ArchivesPage() {
 
       <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-8">
         <div>
-          <div className="flex items-center gap-3">
-            <h1 className="text-2xl font-bold text-white">Archives</h1>
+          <div className="flex items-start gap-3">
+            <div>
+              <h1 className="text-2xl font-bold text-white flex items-center gap-3">
+                <ArchiveIcon className="w-7 h-7 text-bambu-green" />
+                Archives
+              </h1>
+              <p className="text-bambu-gray mt-1">
+                {filteredArchives?.length || 0} of {archives?.length || 0} prints
+              </p>
+            </div>
             <select
-              className="px-3 py-1.5 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-bambu-gray-light text-sm focus:border-bambu-green focus:outline-none"
+              className="mt-0.5 px-3 py-1.5 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-bambu-gray-light text-sm focus:border-bambu-green focus:outline-none"
               value={collection}
               onChange={(e) => setCollection(e.target.value as Collection)}
             >
@@ -3090,9 +3099,6 @@ export function ArchivesPage() {
               ))}
             </select>
           </div>
-          <p className="text-bambu-gray">
-            {filteredArchives?.length || 0} of {archives?.length || 0} prints
-          </p>
         </div>
         <div className="flex items-center gap-2 sm:gap-3 flex-wrap">
           {/* Export dropdown */}

+ 2 - 4
frontend/src/pages/FileManagerPage.tsx

@@ -1410,12 +1410,10 @@ export function FileManagerPage() {
       <div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 mb-6">
         <div>
           <h1 className="text-2xl font-bold text-white flex items-center gap-3">
-            <div className="p-2.5 bg-bambu-green/10 rounded-xl">
-              <FolderOpen className="w-6 h-6 text-bambu-green" />
-            </div>
+            <FolderOpen className="w-7 h-7 text-bambu-green" />
             {t('fileManager.title')}
           </h1>
-          <p className="text-sm text-bambu-gray mt-2 ml-14">
+          <p className="text-bambu-gray mt-1">
             {t('fileManager.subtitle')}
           </p>
         </div>

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

@@ -1018,15 +1018,15 @@ function InventoryPage({ spoolmanMode = false, spoolmanModeReady = true }: { spo
   };
 
   return (
-    <div className="p-4 md:p-6 space-y-6">
+    <div className="p-4 md:p-8 space-y-6">
       {/* Header */}
       <div className="flex items-center justify-between">
         <div>
-          <div className="flex items-center gap-3">
-            <Package className="w-6 h-6 text-bambu-green" />
-            <h1 className="text-2xl font-bold text-white">{t('inventory.title')}</h1>
-          </div>
-          <p className="text-sm text-bambu-gray mt-1 ml-9">{t('inventory.noSpools').split('.')[0] ? '' : ''}</p>
+          <h1 className="text-2xl font-bold text-white flex items-center gap-3">
+            <Package className="w-7 h-7 text-bambu-green" />
+            {t('inventory.title')}
+          </h1>
+          <p className="text-bambu-gray mt-1">{t('inventory.subtitle')}</p>
         </div>
         <div className="flex items-center gap-2">
           <Button

+ 5 - 2
frontend/src/pages/MaintenancePage.tsx

@@ -1227,8 +1227,11 @@ export function MaintenancePage() {
     <div className="p-4 md:p-8">
       {/* Header */}
       <div className="mb-6">
-        <h1 className="text-2xl font-bold text-white">{t('maintenance.title')}</h1>
-        <p className="text-bambu-gray text-sm mt-1">
+        <h1 className="text-2xl font-bold text-white flex items-center gap-3">
+          <Wrench className="w-7 h-7 text-bambu-green" />
+          {t('maintenance.title')}
+        </h1>
+        <p className="text-bambu-gray mt-1">
           {activeTab === 'status' ? (
             <>
               {totalDue > 0 && <span className="text-red-400">{t('maintenance.dueCount', { count: totalDue })}</span>}

+ 9 - 8
frontend/src/pages/MakerworldPage.tsx

@@ -408,16 +408,17 @@ export function MakerworldPage() {
   const downloadCount = pickNumber(design, 'downloadCount');
 
   return (
-    <div className="p-6 max-w-screen-2xl mx-auto space-y-6">
-      <div className="flex items-center gap-3">
-        <Globe className="w-7 h-7 text-brand-500" />
-        <h1 className="text-2xl font-bold">{t('makerworld.title')}</h1>
+    <div className="p-4 md:p-8 max-w-screen-2xl space-y-6">
+      <div>
+        <h1 className="text-2xl font-bold text-white flex items-center gap-3">
+          <Globe className="w-7 h-7 text-bambu-green" />
+          {t('makerworld.title')}
+        </h1>
+        <p className="text-bambu-gray mt-1">
+          {t('makerworld.description')}
+        </p>
       </div>
 
-      <p className="text-sm text-gray-600 dark:text-gray-400">
-        {t('makerworld.description')}
-      </p>
-
       {/* Two-column layout: main flow on the left, sticky "Recent imports"
           sidebar on the right at lg+. Collapses to single column on narrow
           screens (tablet/phone), with the sidebar tucked below the main flow. */}

+ 6 - 3
frontend/src/pages/ProfilesPage.tsx

@@ -2868,11 +2868,14 @@ export function ProfilesPage() {
   }
 
   return (
-    <div className="p-6 lg:p-8">
+    <div className="p-4 md:p-8">
       {/* Page Header */}
       <div className="mb-6">
-        <h1 className="text-2xl font-bold text-white">{t('profiles.title')}</h1>
-        <p className="text-bambu-gray">{t('profiles.subtitle')}</p>
+        <h1 className="text-2xl font-bold text-white flex items-center gap-3">
+          <Cloud className="w-7 h-7 text-bambu-green" />
+          {t('profiles.title')}
+        </h1>
+        <p className="text-bambu-gray mt-1">{t('profiles.subtitle')}</p>
       </div>
 
       {/* Tab Navigation */}

+ 2 - 4
frontend/src/pages/ProjectsPage.tsx

@@ -1028,12 +1028,10 @@ export function ProjectsPage() {
       <div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
         <div>
           <h1 className="text-2xl font-bold text-white flex items-center gap-3">
-            <div className="p-2.5 bg-bambu-green/10 rounded-xl">
-              <FolderKanban className="w-6 h-6 text-bambu-green" />
-            </div>
+            <FolderKanban className="w-7 h-7 text-bambu-green" />
             {t('projects.title')}
           </h1>
-          <p className="text-sm text-bambu-gray mt-2 ml-14">
+          <p className="text-bambu-gray mt-1">
             {t('projects.subtitle')}
           </p>
         </div>

+ 7 - 4
frontend/src/pages/SettingsPage.tsx

@@ -1230,11 +1230,14 @@ export function SettingsPage() {
 
   return (
     <CardDensityProvider density="dense">
-    <div className="p-4 md:p-6">
+    <div className="p-4 md:p-8">
       <div className="mb-4 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
-        <div className="flex items-baseline gap-3">
-          <h1 className="text-2xl font-bold text-white">{t('settings.title')}</h1>
-          <p className="text-sm text-bambu-gray hidden md:block">{t('settings.configureBambuddy')}</p>
+        <div>
+          <h1 className="text-2xl font-bold text-white flex items-center gap-3">
+            <SettingsIcon className="w-7 h-7 text-bambu-green" />
+            {t('settings.title')}
+          </h1>
+          <p className="text-bambu-gray mt-1">{t('settings.configureBambuddy')}</p>
         </div>
         {/* Cross-tab search */}
         <div className="relative sm:w-72">

+ 6 - 2
frontend/src/pages/StatsPage.tsx

@@ -20,6 +20,7 @@ import {
   Calendar,
   ChevronDown,
   Users,
+  BarChart3,
 } from 'lucide-react';
 import {
   BarChart,
@@ -1148,10 +1149,13 @@ export function StatsPage() {
       <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6">
         <div>
           <div className="flex items-center gap-2">
-            <h1 className="text-2xl font-bold text-white">{t('stats.title')}</h1>
+            <h1 className="text-2xl font-bold text-white flex items-center gap-3">
+              <BarChart3 className="w-7 h-7 text-bambu-green" />
+              {t('stats.title')}
+            </h1>
             {isRefetching && <Loader2 className="w-5 h-5 text-bambu-green animate-spin" />}
           </div>
-          <p className="text-bambu-gray">{t('stats.subtitle')}</p>
+          <p className="text-bambu-gray mt-1">{t('stats.subtitle')}</p>
         </div>
         <div className="flex items-center gap-2 flex-wrap">
           {/* Hidden widgets button - toggles panel in Dashboard */}

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


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


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


+ 2 - 2
static/index.html

@@ -26,8 +26,8 @@
 
     <!-- Splash screens for iOS -->
     <link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
-    <script type="module" crossorigin src="/assets/index-J0xD4Xg0.js"></script>
-    <link rel="stylesheet" crossorigin href="/assets/index-DdYV-omj.css">
+    <script type="module" crossorigin src="/assets/index-DPn2Wuq0.js"></script>
+    <link rel="stylesheet" crossorigin href="/assets/index-BkYu3kLs.css">
   </head>
   <body>
     <div id="root"></div>

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