Fix platform UI consistency regressions

This commit is contained in:
rcourtman
2026-05-20 11:46:30 +01:00
parent ecd44945bc
commit 111af289ab
20 changed files with 109 additions and 56 deletions
@@ -895,7 +895,9 @@ describe('GUEST_COLUMNS', () => {
expect(nameColumn?.width).toBe('200px'); expect(nameColumn?.width).toBe('200px');
expect(nameColumn?.minWidth).toBe('180px'); expect(nameColumn?.minWidth).toBe('180px');
expect(nameColumn?.maxWidth).toBe('220px'); expect(nameColumn?.maxWidth).toBe('220px');
expect(runtimeColumn?.width).toBe('84px'); expect(runtimeColumn?.width).toBe('104px');
expect(runtimeColumn?.minWidth).toBe('96px');
expect(runtimeColumn?.maxWidth).toBe('112px');
expect(typeColumn?.width).toBe('60px'); expect(typeColumn?.width).toBe('60px');
expect(netIoColumn?.width).toBe('170px'); expect(netIoColumn?.width).toBe('170px');
expect(netIoColumn?.minWidth).toBe('170px'); expect(netIoColumn?.minWidth).toBe('170px');
@@ -985,7 +987,7 @@ describe('GUEST_COLUMNS', () => {
expect( expect(
getGuestColumnWidthStyle('update', false, 'compact', compactDockerRuntimeColumnIds), getGuestColumnWidthStyle('update', false, 'compact', compactDockerRuntimeColumnIds),
).toEqual({ ).toEqual({
width: '8.2569%', width: '8.9286%',
}); });
}); });
@@ -166,9 +166,9 @@ export const GUEST_COLUMNS: ColumnDef[] = [
{ {
id: 'runtime', id: 'runtime',
label: 'Runtime', label: 'Runtime',
width: '84px', width: '104px',
minWidth: '74px', minWidth: '96px',
maxWidth: '96px', maxWidth: '112px',
toggleable: true, toggleable: true,
kind: 'text', kind: 'text',
}, },
@@ -386,7 +386,7 @@ const GUEST_COLUMN_RESPONSIVE_WEIGHTS: Record<
}, },
compact: { compact: {
name: 26, name: 26,
runtime: 8, runtime: 10,
type: 7, type: 7,
info: 7, info: 7,
vmid: 7, vmid: 7,
@@ -398,7 +398,7 @@ const GUEST_COLUMN_RESPONSIVE_WEIGHTS: Record<
image: 18, image: 18,
namespace: 11, namespace: 11,
context: 13, context: 13,
update: 9, update: 10,
}, },
}; };
@@ -157,7 +157,7 @@ export const DockerHostsTable: Component<{
> >
<TableCard class={PLATFORM_TABLE_CARD_CLASS}> <TableCard class={PLATFORM_TABLE_CARD_CLASS}>
<TableCardHeader title={props.title ?? 'Hosts'} /> <TableCardHeader title={props.title ?? 'Hosts'} />
<Table class="min-w-full table-fixed text-xs md:min-w-[1080px]"> <Table class="min-w-full table-fixed text-xs md:min-w-[1240px]">
<TableHeader> <TableHeader>
<TableRow class={PLATFORM_TABLE_HEADER_ROW_CLASS}> <TableRow class={PLATFORM_TABLE_HEADER_ROW_CLASS}>
{/* {/*
@@ -7,6 +7,7 @@ import { useWorkloadsState } from '@/components/Workloads/useWorkloadsState';
import { useUnifiedResources } from '@/hooks/useUnifiedResources'; import { useUnifiedResources } from '@/hooks/useUnifiedResources';
import { import {
PlatformErrorState, PlatformErrorState,
PlatformSectionTabs,
PlatformTableEmptyState, PlatformTableEmptyState,
} from '@/features/platformPage/sharedPlatformPage'; } from '@/features/platformPage/sharedPlatformPage';
import { DockerHostsTable } from './DockerHostsTable'; import { DockerHostsTable } from './DockerHostsTable';
@@ -34,6 +35,7 @@ const DOCKER_WORKLOAD_STATUS_OPTIONS: readonly WorkloadsStatusOption[] = [
{ value: 'degraded', label: 'Attention' }, { value: 'degraded', label: 'Attention' },
{ value: 'stopped', label: 'Stopped' }, { value: 'stopped', label: 'Stopped' },
]; ];
const DOCKER_TAB_SPECS = [{ id: 'overview', label: 'Overview', path: '/docker/overview' }] as const;
const dockerIcon = () => <ContainerIcon class="h-6 w-6 text-slate-400" />; const dockerIcon = () => <ContainerIcon class="h-6 w-6 text-slate-400" />;
@@ -90,6 +92,12 @@ export function DockerPageSurface() {
return ( return (
<div data-testid="docker-page" class="space-y-3"> <div data-testid="docker-page" class="space-y-3">
<PlatformSectionTabs
tabs={DOCKER_TAB_SPECS}
active="overview"
ariaLabel="Docker sections"
/>
<Show <Show
when={!loading() || model().resources.length > 0} when={!loading() || model().resources.length > 0}
fallback={ fallback={
@@ -124,7 +124,7 @@ export const DockerServicesTable: Component<{
> >
<TableCard class={PLATFORM_TABLE_CARD_CLASS}> <TableCard class={PLATFORM_TABLE_CARD_CLASS}>
<TableCardHeader title={props.title ?? 'Swarm Services'} /> <TableCardHeader title={props.title ?? 'Swarm Services'} />
<Table class="min-w-full table-fixed text-xs md:min-w-[900px]"> <Table class="min-w-full table-fixed text-xs md:min-w-[1080px]">
<TableHeader> <TableHeader>
<TableRow class={PLATFORM_TABLE_HEADER_ROW_CLASS}> <TableRow class={PLATFORM_TABLE_HEADER_ROW_CLASS}>
{/* {/*
@@ -30,6 +30,16 @@ vi.mock('@/components/Workloads/WorkloadsSurface', () => ({
), ),
})); }));
vi.mock('@/features/platformPage/sharedPlatformPage', async () => {
const actual = await vi.importActual<typeof import('@/features/platformPage/sharedPlatformPage')>(
'@/features/platformPage/sharedPlatformPage',
);
return {
...actual,
PlatformSectionTabs: () => <div data-testid="docker-section-tabs" />,
};
});
const makeDockerHost = (overrides: Partial<Resource> = {}): Resource => ({ const makeDockerHost = (overrides: Partial<Resource> = {}): Resource => ({
id: 'agent:docker-01', id: 'agent:docker-01',
name: 'docker-01', name: 'docker-01',
@@ -21,8 +21,8 @@ import {
PlatformTableEmptyState, PlatformTableEmptyState,
createPlatformTableFilterState, createPlatformTableFilterState,
filterPlatformResources, filterPlatformResources,
getPlatformTableCellClass, getPlatformTableCellClassForKind,
getPlatformTableHeadClass, getPlatformTableHeadClassForKind,
type PlatformResourceStatusFilter, type PlatformResourceStatusFilter,
} from '@/features/platformPage/sharedPlatformPage'; } from '@/features/platformPage/sharedPlatformPage';
import { import {
@@ -144,25 +144,33 @@ export const KubernetesClustersTable: Component<{
> >
<TableCard class={PLATFORM_TABLE_CARD_CLASS}> <TableCard class={PLATFORM_TABLE_CARD_CLASS}>
<TableCardHeader title={props.title ?? 'Clusters'} /> <TableCardHeader title={props.title ?? 'Clusters'} />
<Table class="min-w-full table-fixed text-xs md:min-w-[860px]"> <Table class="min-w-full table-fixed text-xs md:min-w-[1040px]">
<TableHeader> <TableHeader>
<TableRow class={PLATFORM_TABLE_HEADER_ROW_CLASS}> <TableRow class={PLATFORM_TABLE_HEADER_ROW_CLASS}>
<TableHead class={getPlatformTableHeadClass()}>Cluster</TableHead> <TableHead class={`${getPlatformTableHeadClassForKind('name')} md:w-[17%]`}>
<TableHead class={`${getPlatformTableHeadClass()} hidden md:table-cell`}> Cluster
</TableHead>
<TableHead class={`${getPlatformTableHeadClassForKind('text')} hidden md:table-cell md:w-[15%]`}>
Context Context
</TableHead> </TableHead>
<TableHead class={`${getPlatformTableHeadClass()} hidden md:table-cell`}> <TableHead class={`${getPlatformTableHeadClassForKind('text')} hidden md:table-cell md:w-[10%]`}>
Version Version
</TableHead> </TableHead>
<TableHead class={getPlatformTableHeadClass('right')}>Nodes</TableHead> <TableHead class={`${getPlatformTableHeadClassForKind('numeric-value')} md:w-[8%]`}>
<TableHead class={`${getPlatformTableHeadClass('right')} hidden md:table-cell`}> Nodes
</TableHead>
<TableHead class={`${getPlatformTableHeadClassForKind('numeric-value')} hidden md:table-cell md:w-[8%]`}>
Pods Pods
</TableHead> </TableHead>
<TableHead class={`${getPlatformTableHeadClass('right')} hidden md:table-cell`}> <TableHead class={`${getPlatformTableHeadClassForKind('numeric-value')} hidden md:table-cell md:w-[12%]`}>
Deployments Deployments
</TableHead> </TableHead>
<TableHead class={getPlatformTableHeadClass('right')}>CPU</TableHead> <TableHead class={`${getPlatformTableHeadClassForKind('metric-bar')} md:w-[15%]`}>
<TableHead class={getPlatformTableHeadClass('right')}>Memory</TableHead> CPU
</TableHead>
<TableHead class={`${getPlatformTableHeadClassForKind('metric-bar')} md:w-[15%]`}>
Memory
</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody class={PLATFORM_TABLE_BODY_CLASS}> <TableBody class={PLATFORM_TABLE_BODY_CLASS}>
@@ -194,7 +202,7 @@ export const KubernetesClustersTable: Component<{
onKeyDown={drawer.handleActivationKey(cluster)} onKeyDown={drawer.handleActivationKey(cluster)}
tabIndex={0} tabIndex={0}
> >
<TableCell class={getPlatformTableCellClass()}> <TableCell class={getPlatformTableCellClassForKind('name')}>
<div class="flex min-w-0 items-center gap-2"> <div class="flex min-w-0 items-center gap-2">
<StatusDot <StatusDot
size="sm" size="sm"
@@ -211,37 +219,37 @@ export const KubernetesClustersTable: Component<{
</div> </div>
</TableCell> </TableCell>
<TableCell <TableCell
class={`${getPlatformTableCellClass()} hidden text-base-content md:table-cell`} class={`${getPlatformTableCellClassForKind('text')} hidden text-base-content md:table-cell`}
> >
{context()} {context()}
</TableCell> </TableCell>
<TableCell <TableCell
class={`${getPlatformTableCellClass()} hidden font-mono text-[11px] text-base-content md:table-cell`} class={`${getPlatformTableCellClassForKind('text')} hidden font-mono text-[11px] text-base-content md:table-cell`}
> >
{version()} {version()}
</TableCell> </TableCell>
<TableCell <TableCell
class={`${getPlatformTableCellClass('right')} text-base-content tabular-nums`} class={`${getPlatformTableCellClassForKind('numeric-value')} text-base-content tabular-nums`}
> >
{counts().nodes} {counts().nodes}
</TableCell> </TableCell>
<TableCell <TableCell
class={`${getPlatformTableCellClass('right')} hidden text-base-content tabular-nums md:table-cell`} class={`${getPlatformTableCellClassForKind('numeric-value')} hidden text-base-content tabular-nums md:table-cell`}
> >
{counts().pods} {counts().pods}
</TableCell> </TableCell>
<TableCell <TableCell
class={`${getPlatformTableCellClass('right')} hidden text-base-content tabular-nums md:table-cell`} class={`${getPlatformTableCellClassForKind('numeric-value')} hidden text-base-content tabular-nums md:table-cell`}
> >
{counts().deployments} {counts().deployments}
</TableCell> </TableCell>
<TableCell <TableCell
class={`${getPlatformTableCellClass('right')} text-base-content`} class={`${getPlatformTableCellClassForKind('metric-bar')} text-base-content`}
> >
{formatPercent(cluster.cpu?.current)} {formatPercent(cluster.cpu?.current)}
</TableCell> </TableCell>
<TableCell <TableCell
class={`${getPlatformTableCellClass('right')} text-base-content`} class={`${getPlatformTableCellClassForKind('metric-bar')} text-base-content`}
> >
{formatPercent(cluster.memory?.current)} {formatPercent(cluster.memory?.current)}
</TableCell> </TableCell>
@@ -91,7 +91,7 @@ export const KubernetesDeploymentsTable: Component<{
> >
<TableCard class={PLATFORM_TABLE_CARD_CLASS}> <TableCard class={PLATFORM_TABLE_CARD_CLASS}>
<TableCardHeader title={props.title ?? 'Deployments'} /> <TableCardHeader title={props.title ?? 'Deployments'} />
<Table class="min-w-full table-fixed text-xs md:min-w-[820px]"> <Table class="min-w-full table-fixed text-xs md:min-w-[1080px]">
<TableHeader> <TableHeader>
<TableRow class={PLATFORM_TABLE_HEADER_ROW_CLASS}> <TableRow class={PLATFORM_TABLE_HEADER_ROW_CLASS}>
{/* {/*
@@ -140,7 +140,7 @@ export const KubernetesNodesTable: Component<{
> >
<TableCard class={PLATFORM_TABLE_CARD_CLASS}> <TableCard class={PLATFORM_TABLE_CARD_CLASS}>
<TableCardHeader title={props.title ?? 'Nodes'} /> <TableCardHeader title={props.title ?? 'Nodes'} />
<Table class="min-w-full table-fixed text-xs md:min-w-[1000px]"> <Table class="min-w-full table-fixed text-xs md:min-w-[1280px]">
<TableHeader> <TableHeader>
<TableRow class={PLATFORM_TABLE_HEADER_ROW_CLASS}> <TableRow class={PLATFORM_TABLE_HEADER_ROW_CLASS}>
{/* {/*
@@ -73,8 +73,18 @@ export function PatrolIntelligenceSummary(props: { state: PatrolIntelligenceStat
runtimeState === 'blocked' || runtimeState === 'disabled' || runtimeState === 'unavailable' runtimeState === 'blocked' || runtimeState === 'disabled' || runtimeState === 'unavailable'
); );
}); });
const hasLoadedPatrolEvidence = createMemo(
() =>
summaryStats().hasAnyPatrolFindings ||
(state.patrolRunHistory.value()?.length ?? 0) > 0 ||
Boolean(state.patrolStatus()),
);
const showLoadingSummary = createMemo( const showLoadingSummary = createMemo(
() => !showRuntimeSummary() && !state.intelligenceSummary() && !state.initialSurfaceReady(), () =>
!showRuntimeSummary() &&
!state.intelligenceSummary() &&
!state.initialSurfaceReady() &&
!hasLoadedPatrolEvidence(),
); );
const assessment = createMemo(() => const assessment = createMemo(() =>
getPatrolAssessmentPresentation({ getPatrolAssessmentPresentation({
@@ -112,6 +112,18 @@ describe('PatrolIntelligenceSummary', () => {
expect(screen.queryByTestId('patrol-recommended-next-step-action')).not.toBeInTheDocument(); expect(screen.queryByTestId('patrol-recommended-next-step-action')).not.toBeInTheDocument();
expect(screen.queryByTestId('patrol-summary-details-toggle')).not.toBeInTheDocument(); expect(screen.queryByTestId('patrol-summary-details-toggle')).not.toBeInTheDocument();
}); });
it('does not leave the loading shell behind once patrol evidence has rendered', () => {
const patrolState = {
...createPatrolState(),
initialSurfaceReady: () => false,
intelligenceSummary: () => null,
} satisfies PatrolIntelligenceState;
render(() => <PatrolIntelligenceSummary state={patrolState} />);
expect(screen.queryByTestId('patrol-summary-loading')).not.toBeInTheDocument();
});
}); });
function createPatrolState(): PatrolIntelligenceState { function createPatrolState(): PatrolIntelligenceState {
@@ -70,7 +70,7 @@ describe('platform overview layout guardrails', () => {
expect(source).toContain('PLATFORM_HEALTH_FILTER_OPTIONS'); expect(source).toContain('PLATFORM_HEALTH_FILTER_OPTIONS');
expect(source).not.toContain("from '@/components/shared/SearchInput'"); expect(source).not.toContain("from '@/components/shared/SearchInput'");
expect(source).not.toContain("from '@/components/shared/FilterButtonGroup'"); expect(source).not.toContain("from '@/components/shared/FilterButtonGroup'");
expect(source).not.toContain('createSignal'); expect(source).not.toContain('const [search');
} }
}); });
@@ -97,7 +97,7 @@ describe('platform overview layout guardrails', () => {
expect(vmwarePageSurfaceSource).toContain('<VsphereHostsTable'); expect(vmwarePageSurfaceSource).toContain('<VsphereHostsTable');
expect(vmwarePageSurfaceSource).toContain('<WorkloadsSurface'); expect(vmwarePageSurfaceSource).toContain('<WorkloadsSurface');
expect(vmwarePageSurfaceSource).toContain('<StorageSurface'); expect(vmwarePageSurfaceSource).toContain('<StorageSurface');
expect(vmwarePageSurfaceSource).toContain('forcedView="pools"'); expect(vmwarePageSurfaceSource).not.toContain('forcedView="pools"');
}); });
it('keeps secondary overview tables from rendering duplicate standalone toolbars', () => { it('keeps secondary overview tables from rendering duplicate standalone toolbars', () => {
@@ -113,10 +113,8 @@ describe('platform overview layout guardrails', () => {
it('keeps mobile host tables focused on useful operational columns', () => { it('keeps mobile host tables focused on useful operational columns', () => {
// Assertions use the canonical kind-based helpers // Assertions use the canonical kind-based helpers
// (getPlatformTableHeadClassForKind('<kind>')) for files that have been // (getPlatformTableHeadClassForKind('<kind>')) so the platform overview
// migrated. KubernetesClustersTable still uses the legacy align-based // tables keep aligned metric and numeric columns across providers.
// helper because another agent has it mid-edit; the assertions below
// match that legacy form until its migration lands.
expect(dockerHostsTableSource).toMatch( expect(dockerHostsTableSource).toMatch(
/getPlatformTableHeadClassForKind\('name'\)[\s\S]{0,200}?Host/, /getPlatformTableHeadClassForKind\('name'\)[\s\S]{0,200}?Host/,
); );
@@ -130,11 +128,11 @@ describe('platform overview layout guardrails', () => {
/getPlatformTableHeadClassForKind\('metric-bar'\)[\s\S]{0,200}?Disk/, /getPlatformTableHeadClassForKind\('metric-bar'\)[\s\S]{0,200}?Disk/,
); );
expect(kubernetesClustersTableSource).toContain( expect(kubernetesClustersTableSource).toMatch(
'<TableHead class={getPlatformTableHeadClass()}>Cluster', /getPlatformTableHeadClassForKind\('name'\)[\s\S]{0,200}?Cluster/,
); );
expect(kubernetesClustersTableSource).toContain( expect(kubernetesClustersTableSource).toMatch(
"<TableHead class={getPlatformTableHeadClass('right')}>Nodes", /getPlatformTableHeadClassForKind\('numeric-value'\)[\s\S]{0,200}?Nodes/,
); );
expect(kubernetesNodesTableSource).toMatch( expect(kubernetesNodesTableSource).toMatch(
/getPlatformTableHeadClassForKind\('name'\)[\s\S]{0,200}?Node/, /getPlatformTableHeadClassForKind\('name'\)[\s\S]{0,200}?Node/,
@@ -10,6 +10,7 @@ import {
import { StatusDot } from '@/components/shared/StatusDot'; import { StatusDot } from '@/components/shared/StatusDot';
import { ResponsiveMetricCell } from '@/components/shared/responsive'; import { ResponsiveMetricCell } from '@/components/shared/responsive';
import { TableCard } from '@/components/shared/TableCard'; import { TableCard } from '@/components/shared/TableCard';
import { TableCardHeader } from '@/components/shared/TableCardHeader';
import { NodeDrawer } from '@/components/Workloads/NodeDrawer'; import { NodeDrawer } from '@/components/Workloads/NodeDrawer';
import { StackedMemoryBar } from '@/components/Workloads/StackedMemoryBar'; import { StackedMemoryBar } from '@/components/Workloads/StackedMemoryBar';
import { StackedDiskBar } from '@/components/Workloads/StackedDiskBar'; import { StackedDiskBar } from '@/components/Workloads/StackedDiskBar';
@@ -165,6 +166,7 @@ export const ProxmoxNodesTable: Component<{
} }
> >
<TableCard class={PLATFORM_TABLE_CARD_CLASS}> <TableCard class={PLATFORM_TABLE_CARD_CLASS}>
<TableCardHeader title="Nodes" />
<Table class={`${getProxmoxHostTableMinWidthClass(layoutMode())} table-fixed text-xs`}> <Table class={`${getProxmoxHostTableMinWidthClass(layoutMode())} table-fixed text-xs`}>
<colgroup> <colgroup>
<For each={visibleColumns()}> <For each={visibleColumns()}>
@@ -13,10 +13,10 @@ describe('proxmoxHostTableModel', () => {
expect(ids).toEqual(['node', 'cpu', 'memory', 'disk']); expect(ids).toEqual(['node', 'cpu', 'memory', 'disk']);
expect(getProxmoxHostTableMinWidthClass('mobile')).toBe('min-w-full'); expect(getProxmoxHostTableMinWidthClass('mobile')).toBe('min-w-full');
expect(getProxmoxHostColumnWidthStyle('node', 'mobile', ids)).toEqual({ width: '44%' }); expect(getProxmoxHostColumnWidthStyle('node', 'mobile', ids)).toEqual({ width: '40%' });
expect(getProxmoxHostColumnWidthStyle('cpu', 'mobile', ids)).toEqual({ width: '23%' }); expect(getProxmoxHostColumnWidthStyle('cpu', 'mobile', ids)).toEqual({ width: '20%' });
expect(getProxmoxHostColumnWidthStyle('memory', 'mobile', ids)).toEqual({ width: '23%' }); expect(getProxmoxHostColumnWidthStyle('memory', 'mobile', ids)).toEqual({ width: '20%' });
expect(getProxmoxHostColumnWidthStyle('disk', 'mobile', ids)).toEqual({ width: '10%' }); expect(getProxmoxHostColumnWidthStyle('disk', 'mobile', ids)).toEqual({ width: '20%' });
}); });
it('adds temperature and guest counts before slower-changing metadata on tablet', () => { it('adds temperature and guest counts before slower-changing metadata on tablet', () => {
@@ -47,10 +47,10 @@ describe('proxmoxHostTableModel', () => {
'cts', 'cts',
'cluster', 'cluster',
]); ]);
expect(getProxmoxHostTableMinWidthClass('compact')).toBe('min-w-[1080px]'); expect(getProxmoxHostTableMinWidthClass('compact')).toBe('min-w-[1240px]');
expect(getProxmoxHostTableMinWidthClass('wide')).toBe('min-w-[1080px]'); expect(getProxmoxHostTableMinWidthClass('wide')).toBe('min-w-[1240px]');
expect(getProxmoxHostColumnWidthStyle('cluster', 'compact', compactIds)).toEqual({ expect(getProxmoxHostColumnWidthStyle('cluster', 'compact', compactIds)).toEqual({
width: '13.0435%', width: '10.989%',
}); });
}); });
}); });
@@ -130,7 +130,7 @@ describe('proxmoxPageModel', () => {
]); ]);
}); });
it('hides Proxmox sub-tabs when the connected estate has no matching resource evidence', () => { it('keeps Replication available for a PVE estate without recent replication events', () => {
const model = buildProxmoxPageModel([ const model = buildProxmoxPageModel([
makeResource({ makeResource({
id: 'pve-node-1', id: 'pve-node-1',
@@ -148,7 +148,10 @@ describe('proxmoxPageModel', () => {
}), }),
]); ]);
expect(buildVisibleProxmoxTabSpecs(model).map((tab) => tab.id)).toEqual(['overview']); expect(buildVisibleProxmoxTabSpecs(model).map((tab) => tab.id)).toEqual([
'overview',
'replication',
]);
}); });
it('resolves Proxmox suite scope from canonical platform hints', () => { it('resolves Proxmox suite scope from canonical platform hints', () => {
@@ -122,5 +122,5 @@ export const getProxmoxHostColumnWidthStyle = (
export const getProxmoxHostTableMinWidthClass = ( export const getProxmoxHostTableMinWidthClass = (
layoutMode: WorkloadTableLayoutMode, layoutMode: WorkloadTableLayoutMode,
): 'min-w-full' | 'min-w-[1080px]' => ): 'min-w-full' | 'min-w-[1240px]' =>
layoutMode === 'mobile' || layoutMode === 'tablet' ? 'min-w-full' : 'min-w-[1080px]'; layoutMode === 'mobile' || layoutMode === 'tablet' ? 'min-w-full' : 'min-w-[1240px]';
@@ -373,11 +373,12 @@ export function buildProxmoxPageModel(resources: Resource[]): ProxmoxPageModel {
export function buildVisibleProxmoxTabSpecs(model: ProxmoxPageModel): ProxmoxTabSpec[] { export function buildVisibleProxmoxTabSpecs(model: ProxmoxPageModel): ProxmoxTabSpec[] {
const visible = new Set<ProxmoxPageTabId>(['overview']); const visible = new Set<ProxmoxPageTabId>(['overview']);
const hasPveEstate = model.pveNodes.length > 0 || model.guests.length > 0;
if (model.storage.length > 0 || model.physicalDisks.length > 0) { if (model.storage.length > 0 || model.physicalDisks.length > 0) {
visible.add('storage'); visible.add('storage');
} }
if (model.replicationChanges.length > 0) { if (hasPveEstate || model.replicationChanges.length > 0) {
visible.add('replication'); visible.add('replication');
} }
if (model.resources.some(hasBackupSignal) || model.pbs.length > 0) { if (model.resources.some(hasBackupSignal) || model.pbs.length > 0) {
@@ -163,7 +163,7 @@ export const TrueNASSystemsTable: Component<{
> >
<TableCard class={PLATFORM_TABLE_CARD_CLASS}> <TableCard class={PLATFORM_TABLE_CARD_CLASS}>
<TableCardHeader title={props.title ?? 'Systems'} /> <TableCardHeader title={props.title ?? 'Systems'} />
<Table class="min-w-full table-fixed text-xs md:min-w-[1040px]"> <Table class="min-w-full table-fixed text-xs md:min-w-[1300px]">
<TableHeader> <TableHeader>
<TableRow class={PLATFORM_TABLE_HEADER_ROW_CLASS}> <TableRow class={PLATFORM_TABLE_HEADER_ROW_CLASS}>
{/* {/*
@@ -96,7 +96,6 @@ export function VmwarePageSurface() {
tableOnly tableOnly
showFilterToolbar showFilterToolbar
forcedSourceFilter={VMWARE_PLATFORM_FILTER} forcedSourceFilter={VMWARE_PLATFORM_FILTER}
forcedView="pools"
filterAriaLabel="vSphere datastore filters" filterAriaLabel="vSphere datastore filters"
filterSearchPlaceholder="Search vSphere datastores by name, host, or capacity group" filterSearchPlaceholder="Search vSphere datastores by name, host, or capacity group"
filterSearchEmptyMessage="Recent vSphere datastore searches appear here." filterSearchEmptyMessage="Recent vSphere datastore searches appear here."
@@ -146,7 +146,7 @@ export const VsphereHostsTable: Component<{
> >
<TableCard class={PLATFORM_TABLE_CARD_CLASS}> <TableCard class={PLATFORM_TABLE_CARD_CLASS}>
<TableCardHeader title={props.title ?? 'Hosts'} /> <TableCardHeader title={props.title ?? 'Hosts'} />
<Table class="min-w-full table-fixed text-xs md:min-w-[960px]"> <Table class="min-w-full table-fixed text-xs md:min-w-[1240px]">
<TableHeader> <TableHeader>
<TableRow class={PLATFORM_TABLE_HEADER_ROW_CLASS}> <TableRow class={PLATFORM_TABLE_HEADER_ROW_CLASS}>
{/* {/*