From bfaa1aa596a0399b740c80fb204ef7c88333a17f Mon Sep 17 00:00:00 2001 From: rcourtman Date: Fri, 15 May 2026 22:33:55 +0100 Subject: [PATCH] Put storage view controls in table header --- .../internal/subsystems/storage-recovery.md | 4 +- .../src/components/Storage/Storage.tsx | 14 ++--- .../components/Storage/StorageContentCard.tsx | 4 +- .../Storage/StorageViewSegmentedControl.tsx | 52 +++++++++++++++++++ .../Storage/__tests__/Storage.test.tsx | 13 +++-- 5 files changed, 73 insertions(+), 14 deletions(-) create mode 100644 frontend-modern/src/components/Storage/StorageViewSegmentedControl.tsx diff --git a/docs/release-control/v6/internal/subsystems/storage-recovery.md b/docs/release-control/v6/internal/subsystems/storage-recovery.md index 838e41e47..f992714ee 100644 --- a/docs/release-control/v6/internal/subsystems/storage-recovery.md +++ b/docs/release-control/v6/internal/subsystems/storage-recovery.md @@ -1047,8 +1047,8 @@ Storage and Recovery can now be embedded by a platform page in table-only mode with a forced Proxmox source/platform filter. The embedded mode suppresses standalone page chrome, summary charts, and full filter chrome, but the Storage surface must keep the canonical Storage / Physical Disks view selector -available unless the embedding explicitly locks a `forcedView`. The canonical -route-backed filter state, fetch builders, table rendering, and +inside the table header unless the embedding explicitly locks a `forcedView`. +The canonical route-backed filter state, fetch builders, table rendering, and storage/recovery vocabulary remain owned by the Storage and Recovery surfaces. Platform pages must compose those owners rather than cloning storage pools, physical disks, recovery events, or protected-inventory tables under diff --git a/frontend-modern/src/components/Storage/Storage.tsx b/frontend-modern/src/components/Storage/Storage.tsx index 312f8ac71..9bb2f78bd 100644 --- a/frontend-modern/src/components/Storage/Storage.tsx +++ b/frontend-modern/src/components/Storage/Storage.tsx @@ -2,8 +2,9 @@ import { Component, Show, createEffect } from 'solid-js'; import StorageCephSection from '@/components/Storage/StorageCephSection'; import StorageContentCard from '@/components/Storage/StorageContentCard'; import StoragePageBanners from '@/components/Storage/StoragePageBanners'; -import StoragePageControls, { StorageViewSwitcher } from '@/components/Storage/StoragePageControls'; +import StoragePageControls from '@/components/Storage/StoragePageControls'; import StoragePageSummary from '@/components/Storage/StoragePageSummary'; +import { StorageViewSegmentedControl } from '@/components/Storage/StorageViewSegmentedControl'; import { PageHeader } from '@/components/shared/PageHeader'; import { StickySummarySection } from '@/components/shared/StickySummarySection'; import { isStorageRecordCeph } from './storagePageState'; @@ -166,12 +167,6 @@ const Storage: Component = (props) => {
- -
- -
-
-
= (props) => { setHoveredStorageResourceId={setHoveredStorageResourceId} selectedDiskId={selectedDiskId} setSelectedDiskId={setSelectedDiskId} + actions={ + props.tableOnly && !props.forcedView && !kioskMode() ? ( + + ) : undefined + } />
diff --git a/frontend-modern/src/components/Storage/StorageContentCard.tsx b/frontend-modern/src/components/Storage/StorageContentCard.tsx index 57f2ea611..384a4c575 100644 --- a/frontend-modern/src/components/Storage/StorageContentCard.tsx +++ b/frontend-modern/src/components/Storage/StorageContentCard.tsx @@ -1,4 +1,4 @@ -import { Component, Show } from 'solid-js'; +import { Component, Show, type JSX } from 'solid-js'; import { TableCardHeader } from '@/components/shared/TableCardHeader'; import { TableCard } from '@/components/shared/TableCard'; import { DiskList } from '@/components/Storage/DiskList'; @@ -48,6 +48,7 @@ type StorageContentCardProps = { setHoveredStorageResourceId: (value: string | null) => void; selectedDiskId: () => string | null; setSelectedDiskId: (value: string | null) => void; + actions?: JSX.Element; }; export const StorageContentCard: Component = (props) => { @@ -66,6 +67,7 @@ export const StorageContentCard: Component = (props) => > diff --git a/frontend-modern/src/components/Storage/StorageViewSegmentedControl.tsx b/frontend-modern/src/components/Storage/StorageViewSegmentedControl.tsx new file mode 100644 index 000000000..8e9ce772c --- /dev/null +++ b/frontend-modern/src/components/Storage/StorageViewSegmentedControl.tsx @@ -0,0 +1,52 @@ +import { splitProps, type Component, type JSX } from 'solid-js'; +import DatabaseIcon from 'lucide-solid/icons/database'; +import HardDriveIcon from 'lucide-solid/icons/hard-drive'; +import { FilterSegmentedControl } from '@/components/shared/FilterToolbar'; +import type { StorageView } from './storagePageState'; + +interface StorageViewSegmentedControlProps extends Omit< + JSX.HTMLAttributes, + 'onChange' +> { + value: StorageView; + onChange: (value: StorageView) => void; +} + +export const StorageViewSegmentedControl: Component = (props) => { + const [local, divProps] = splitProps(props, ['value', 'onChange']); + + return ( + local.onChange(value as StorageView)} + options={[ + { + value: 'pools', + title: 'Show storage pools and backup targets', + label: ( + <> +