diff --git a/client/src/pages/AdminPage.test.tsx b/client/src/pages/AdminPage.test.tsx index 1cd09f0f4..bffee1972 100644 --- a/client/src/pages/AdminPage.test.tsx +++ b/client/src/pages/AdminPage.test.tsx @@ -69,6 +69,10 @@ vi.mock('../components/Admin/DefaultUserSettingsTab', () => ({ default: () =>
, })); +vi.mock('../components/Admin/storage/AdminStoragePanel', () => ({ + default: () =>
, +})); + beforeEach(() => { resetAllStores(); }); @@ -1631,4 +1635,19 @@ describe('AdminPage', () => { expect(screen.queryByText('Files')).not.toBeInTheDocument(); }); }); + + it('FE-PAGE-ADMIN-STOR-001: the Storage tab renders the storage panel', async () => { + seedStore(useAuthStore, { isAuthenticated: true, user: buildAdmin() }); + render(); + const tab = await screen.findByRole('button', { name: /^storage$/i }); + fireEvent.click(tab); + expect(screen.getByTestId('admin-storage-panel')).toBeInTheDocument(); + }); + + it('FE-PAGE-ADMIN-STOR-002: managed mode hides the Storage tab (hoster-level config)', async () => { + seedStore(useAuthStore, { isAuthenticated: true, user: buildAdmin(), managed: true }); + render(); + await screen.findByRole('button', { name: /^users$/i }); + expect(screen.queryByRole('button', { name: /^storage$/i })).not.toBeInTheDocument(); + }); }); diff --git a/client/src/pages/AdminPage.tsx b/client/src/pages/AdminPage.tsx index 9fc153137..16e31f8a6 100644 --- a/client/src/pages/AdminPage.tsx +++ b/client/src/pages/AdminPage.tsx @@ -12,7 +12,8 @@ import PackingTemplateManager from '../components/Admin/PackingTemplateManager' import AuditLogPanel from '../components/Admin/AuditLogPanel' import AdminMcpTokensPanel from '../components/Admin/AdminMcpTokensPanel' import AdminPluginsPanel from '../components/Admin/AdminPluginsPanel' -import { Users, Map, Briefcase, Shield, FileText, SlidersHorizontal, UserCog, Puzzle, Blocks, Settings as SettingsIcon, Bell, Database, ScrollText, KeyRound, GitBranch, Bug } from 'lucide-react' +import AdminStoragePanel from '../components/Admin/storage/AdminStoragePanel' +import { Users, Map, Briefcase, Shield, FileText, SlidersHorizontal, UserCog, Puzzle, Blocks, Settings as SettingsIcon, Bell, Database, ScrollText, KeyRound, GitBranch, Bug, HardDrive } from 'lucide-react' import PageSidebar, { type PageSidebarTab } from '../components/Layout/PageSidebar' import { useAdmin } from './admin/useAdmin' import AdminUpdateBanner from './admin/AdminUpdateBanner' @@ -55,6 +56,9 @@ function AdminPageDesktop(): React.ReactElement { { id: 'settings', label: t('admin.tabs.settings'), icon: SettingsIcon, group: gConfig }, { id: 'addons', label: t('admin.tabs.addons'), icon: Puzzle, group: gConfig }, { id: 'plugins', label: t('admin.tabs.plugins'), icon: Blocks, group: gConfig }, + // Storage backends and their credentials are hoster-level configuration — + // the server refuses the whole surface in managed mode (MANAGED_FORBIDDEN). + ...(managed ? [] : [{ id: 'storage', label: t('admin.tabs.storage'), icon: HardDrive, group: gConfig }]), { id: 'notifications', label: t('admin.tabs.notifications'), icon: Bell, group: gIntegration }, ...(mcpEnabled ? [{ id: 'mcp-tokens', label: t('admin.tabs.mcpTokens'), icon: KeyRound, group: gIntegration }] : []), // Releases and update cadence belong to whoever operates the install. @@ -180,6 +184,8 @@ function AdminPageDesktop(): React.ReactElement { {activeTab === 'plugins' && } + {activeTab === 'storage' && } + {activeTab === 'github' && } {activeTab === 'defaults' && }