瀏覽代碼

Post work PR #1448

maziggy 4 周之前
父節點
當前提交
04009a5c6a

文件差異過大導致無法顯示
+ 0 - 0
CHANGELOG.md


+ 37 - 0
frontend/src/__tests__/components/Layout.test.tsx

@@ -159,6 +159,43 @@ describe('Layout', () => {
     });
   });
 
+  describe('finance nav item', () => {
+    it('stays out of the sidebar while billing is off', async () => {
+      // billing_enabled defaults to false and the Finance page has nothing to
+      // show without it, so the entry must not be there at all.
+      render(<Layout />);
+
+      await waitFor(() => {
+        expect(document.querySelector('aside a[href="/stats"]')).toBeInTheDocument();
+      });
+      expect(document.querySelector('aside a[href="/finance"]')).toBeNull();
+    });
+
+    it('appears between Statistics and Settings once billing is on', async () => {
+      server.use(
+        http.get('/api/v1/settings/', () =>
+          HttpResponse.json({
+            check_updates: false,
+            check_printer_firmware: false,
+            auto_archive: true,
+            billing_enabled: true,
+          }),
+        ),
+      );
+
+      render(<Layout />);
+
+      await waitFor(() => {
+        expect(document.querySelector('aside a[href="/finance"]')).toBeInTheDocument();
+      });
+
+      const sidebar = document.querySelector('aside');
+      const hrefs = Array.from(sidebar?.querySelectorAll('a[href]') ?? []).map((a) => a.getAttribute('href'));
+      expect(hrefs.indexOf('/finance')).toBeGreaterThan(hrefs.indexOf('/stats'));
+      expect(hrefs.indexOf('/finance')).toBeLessThan(hrefs.indexOf('/settings'));
+    });
+  });
+
   describe('version display', () => {
     it('shows version info', async () => {
       render(<Layout />);

+ 3 - 3
frontend/src/__tests__/pages/SettingsPage.test.tsx

@@ -368,7 +368,7 @@ describe('SettingsPage', () => {
 
       expect(localStorage.setItem).toHaveBeenCalledWith(
         SIDEBAR_ORDER_KEY,
-        JSON.stringify(['ext-7', 'printers', 'inventory', 'archives', 'queue', 'projects', 'finance', 'files', 'makerworld', 'profiles', 'maintenance', 'stats', 'notifications', 'settings']),
+        JSON.stringify(['ext-7', 'printers', 'inventory', 'archives', 'queue', 'projects', 'files', 'makerworld', 'profiles', 'maintenance', 'stats', 'finance', 'notifications', 'settings']),
       );
     });
 
@@ -410,7 +410,7 @@ describe('SettingsPage', () => {
       expect(localStorage.setItem).toHaveBeenCalledWith(SIDEBAR_HIDDEN_SYSTEM_ITEMS_KEY, JSON.stringify([]));
       expect(localStorage.setItem).toHaveBeenCalledWith(
         SIDEBAR_ORDER_KEY,
-        JSON.stringify(['printers', 'inventory', 'archives', 'queue', 'projects', 'finance', 'files', 'makerworld', 'profiles', 'maintenance', 'stats', 'notifications', 'settings', 'ext-7']),
+        JSON.stringify(['printers', 'inventory', 'archives', 'queue', 'projects', 'files', 'makerworld', 'profiles', 'maintenance', 'stats', 'finance', 'notifications', 'settings', 'ext-7']),
       );
 
       const settingsRow = screen.getAllByText('Settings')
@@ -476,12 +476,12 @@ describe('SettingsPage', () => {
           'archives',
           'queue',
           'projects',
-          'finance',
           'files',
           'makerworld',
           'profiles',
           'maintenance',
           'stats',
+          'finance',
           'notifications',
           'settings',
         ],

+ 9 - 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, ListOrdered, BarChart3, Cloud, Settings, Sun, Moon, Monitor, ChevronLeft, ChevronRight, Keyboard, Github, ArrowUpCircle, Wrench, FolderKanban, FolderOpen, X, Menu, Info, Plug, Bug, LogOut, Key, Loader2, Disc3, ShieldAlert, Globe, Bell, Wallet, type LucideIcon } from 'lucide-react';
+import { Printer, Archive, ListOrdered, BarChart3, Cloud, Settings, Sun, Moon, Monitor, ChevronLeft, ChevronRight, Keyboard, Github, ArrowUpCircle, Wrench, FolderKanban, FolderOpen, X, Menu, Info, Plug, Bug, LogOut, Key, Loader2, Disc3, ShieldAlert, Globe, Bell, Receipt, type LucideIcon } from 'lucide-react';
 import { useTranslation } from 'react-i18next';
 import { useTheme } from '../contexts/ThemeContext';
 import { KeyboardShortcutsModal } from './KeyboardShortcutsModal';
@@ -43,12 +43,14 @@ export const defaultNavItems: NavItem[] = [
   { id: 'archives', to: '/archives', icon: Archive, labelKey: 'nav.archives' },
   { id: 'queue', to: '/queue', icon: ListOrdered, labelKey: 'nav.queue' },
   { id: 'projects', to: '/projects', icon: FolderKanban, labelKey: 'nav.projects' },
-  { id: 'finance', to: '/finance', icon: Wallet, labelKey: 'nav.finance' },
   { id: 'files', to: '/files', icon: FolderOpen, labelKey: 'nav.files' },
   { id: 'makerworld', to: '/makerworld', icon: Globe, labelKey: 'nav.makerworld' },
   { id: 'profiles', to: '/profiles', icon: Cloud, labelKey: 'nav.profiles' },
   { id: 'maintenance', to: '/maintenance', icon: Wrench, labelKey: 'nav.maintenance' },
   { id: 'stats', to: '/stats', icon: BarChart3, labelKey: 'nav.stats' },
+  // Opt-in feature: gated in isHidden() on the billing_enabled setting, so the
+  // entry stays out of the sidebar entirely until an admin turns billing on.
+  { id: 'finance', to: '/finance', icon: Receipt, labelKey: 'nav.finance' },
   // User-account feature: gated in isHidden() on advanced auth + user_notifications
   // + the notifications:user_email permission. Kept adjacent to Settings
   // intentionally. Do not drop this entry — without it the /notifications page
@@ -336,6 +338,11 @@ export function Layout() {
       }
       // notifications nav item also requires advanced auth to be enabled and user_notifications_enabled setting
       if (id === 'notifications' && (!authEnabled || !advancedAuthStatus?.advanced_auth_enabled || (settings?.user_notifications_enabled === false))) return true;
+      // Finance is off by default and the page is meaningless without it, so it
+      // stays hidden until billing is explicitly on. Tested for `true` rather
+      // than `!== false` on purpose: settings are undefined on the first render,
+      // and a nav entry that appears and then vanishes reads as a glitch.
+      if (id === 'finance' && settings?.billing_enabled !== true) return true;
       return false;
     };
 

文件差異過大導致無法顯示
+ 1 - 0
static/assets/index-DZYWm6I1.css


文件差異過大導致無法顯示
+ 0 - 0
static/assets/index-WBZDnFeY.js


文件差異過大導致無法顯示
+ 0 - 1
static/assets/index-ud1tvgv1.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-CDkM7wuh.js"></script>
-    <link rel="stylesheet" crossorigin href="/assets/index-ud1tvgv1.css">
+    <script type="module" crossorigin src="/assets/index-WBZDnFeY.js"></script>
+    <link rel="stylesheet" crossorigin href="/assets/index-DZYWm6I1.css">
   </head>
   <body>
     <div id="root"></div>

部分文件因文件數量過多而無法顯示