Fit TrueNAS detail tables

This commit is contained in:
rcourtman
2026-05-21 16:50:40 +01:00
parent 64a3fbc07e
commit 2e5e54d598
5 changed files with 41 additions and 77 deletions
@@ -16,6 +16,8 @@ import proxmoxPageSurfaceSource from '@/features/proxmox/ProxmoxPageSurface.tsx?
import proxmoxReplicationTableSource from '@/features/proxmox/ProxmoxReplicationTable.tsx?raw';
import sharedPlatformPageSource from '@/features/platformPage/sharedPlatformPage.tsx?raw';
import truenasAlertsTableSource from '@/features/truenas/TrueNASAlertsTable.tsx?raw';
import truenasAppsTableSource from '@/features/truenas/TrueNASAppsTable.tsx?raw';
import truenasNetworkSharesTableSource from '@/features/truenas/TrueNASNetworkSharesTable.tsx?raw';
import truenasPageSurfaceSource from '@/features/truenas/TrueNASPageSurface.tsx?raw';
import truenasProtectionTableSource from '@/features/truenas/TrueNASProtectionTable.tsx?raw';
import truenasStorageTopologyTableSource from '@/features/truenas/TrueNASStorageTopologyTable.tsx?raw';
@@ -32,6 +34,8 @@ const platformTableSources = [
kubernetesNodesTableSource,
kubernetesDeploymentsTableSource,
truenasAlertsTableSource,
truenasAppsTableSource,
truenasNetworkSharesTableSource,
truenasProtectionTableSource,
truenasStorageTopologyTableSource,
truenasSystemsTableSource,
@@ -175,8 +179,16 @@ describe('platform overview layout guardrails', () => {
expect(truenasPageSurfaceSource).not.toContain('<StorageSurface');
expect(truenasPageSurfaceSource).not.toContain('<WorkloadsSurface');
expect(truenasPageSurfaceSource).not.toContain('<TrueNASDisksTable');
expect(truenasAppsTableSource).toContain('md:min-w-[960px]');
expect(truenasAppsTableSource).not.toContain('Volumes');
expect(truenasNetworkSharesTableSource).toContain('md:min-w-[960px]');
expect(truenasNetworkSharesTableSource).not.toMatch(
/getPlatformTableHeadClassForKind\('text'\)[\s\S]{0,200}?Dataset/,
);
expect(truenasProtectionTableSource).not.toContain('Signal');
expect(truenasProtectionTableSource).toContain('md:min-w-[960px]');
expect(truenasStorageTopologyTableSource).toContain('md:min-w-[960px]');
expect(truenasVirtualMachinesTableSource).toContain('md:min-w-[960px]');
expect(vmwarePageSurfaceSource).toContain('<VsphereHostsTable');
expect(vmwarePageSurfaceSource).toContain('<WorkloadsSurface');
expect(vmwarePageSurfaceSource).toContain('<StorageSurface');
@@ -33,12 +33,7 @@ import {
createPlatformResourceLabelResolver,
getPlatformResourceDetailRowClass,
} from '@/features/platformPage/PlatformResourceDetailTableRow';
import type {
Resource,
ResourceTrueNASAppMeta,
ResourceTrueNASAppPort,
ResourceTrueNASAppVolume,
} from '@/types/resource';
import type { Resource, ResourceTrueNASAppMeta, ResourceTrueNASAppPort } from '@/types/resource';
import { filterTrueNASApps, type TrueNASAppStatusFilter } from './truenasPageModel';
const TRUENAS_APP_STATUS_OPTIONS: PlatformTableFilterOption<TrueNASAppStatusFilter>[] = [
@@ -80,9 +75,6 @@ const appContainerCount = (app: ResourceTrueNASAppMeta | undefined): number => {
return app?.containers?.length ?? 0;
};
const plural = (count: number, singular: string): string =>
`${count} ${count === 1 ? singular : `${singular}s`}`;
const formatHostPort = (port: ResourceTrueNASAppPort): string => {
const protocol = (asTrimmedString(port.protocol) ?? '').toLowerCase() || 'tcp';
const target = typeof port.containerPort === 'number' ? String(port.containerPort) : '';
@@ -121,24 +113,6 @@ const formatImages = (
return { label: `${visible.join(', ')}${suffix}`, title: images.join(', ') };
};
const volumeLabel = (volume: ResourceTrueNASAppVolume): string => {
const source = asTrimmedString(volume.source);
const destination = asTrimmedString(volume.destination);
if (source && destination) return `${source}->${destination}`;
return source || destination || '';
};
const formatVolumes = (
app: ResourceTrueNASAppMeta | undefined,
): { label: string; title: string } => {
const volumes = app?.volumes ?? [];
if (volumes.length === 0) return { label: '-', title: '' };
return {
label: plural(volumes.length, 'volume'),
title: volumes.map(volumeLabel).filter(Boolean).join(', '),
};
};
const UpdatePills: Component<{ app: ResourceTrueNASAppMeta | undefined }> = (props) => {
const hasAppUpdate = () => props.app?.upgradeAvailable === true;
const hasImageUpdate = () => props.app?.imageUpdatesAvailable === true;
@@ -221,10 +195,10 @@ export const TrueNASAppsTable: Component<{
>
<TableCard class={PLATFORM_TABLE_CARD_CLASS}>
<TableCardHeader title="Apps" />
<Table class="min-w-full table-fixed text-xs md:min-w-[1280px]">
<Table class="min-w-full table-fixed text-xs md:min-w-[960px]">
<TableHeader>
<TableRow class={PLATFORM_TABLE_HEADER_ROW_CLASS}>
<TableHead class={`${getPlatformTableHeadClassForKind('name')} md:w-[16%]`}>
<TableHead class={`${getPlatformTableHeadClassForKind('name')} md:w-[17%]`}>
App
</TableHead>
<TableHead
@@ -232,33 +206,28 @@ export const TrueNASAppsTable: Component<{
>
Version
</TableHead>
<TableHead class={`${getPlatformTableHeadClassForKind('metric-bar')} md:w-[10%]`}>
<TableHead class={`${getPlatformTableHeadClassForKind('metric-bar')} md:w-[9%]`}>
CPU
</TableHead>
<TableHead class={`${getPlatformTableHeadClassForKind('metric-bar')} md:w-[12%]`}>
Memory
</TableHead>
<TableHead
class={`${getPlatformTableHeadClassForKind('numeric-value')} hidden sm:table-cell md:w-[9%]`}
class={`${getPlatformTableHeadClassForKind('numeric-value')} hidden sm:table-cell md:w-[12%]`}
>
Containers
</TableHead>
<TableHead
class={`${getPlatformTableHeadClassForKind('text')} hidden md:table-cell md:w-[16%]`}
class={`${getPlatformTableHeadClassForKind('text')} hidden md:table-cell md:w-[18%]`}
>
Ports
</TableHead>
<TableHead
class={`${getPlatformTableHeadClassForKind('text')} hidden lg:table-cell md:w-[13%]`}
class={`${getPlatformTableHeadClassForKind('text')} hidden lg:table-cell md:w-[12%]`}
>
Images
</TableHead>
<TableHead
class={`${getPlatformTableHeadClassForKind('numeric-value')} hidden xl:table-cell md:w-[7%]`}
>
Volumes
</TableHead>
<TableHead class={`${getPlatformTableHeadClassForKind('badge')} md:w-[7%]`}>
<TableHead class={`${getPlatformTableHeadClassForKind('badge')} md:w-[10%]`}>
Updates
</TableHead>
</TableRow>
@@ -284,7 +253,6 @@ export const TrueNASAppsTable: Component<{
const canRenderMetrics = () => indicator().variant !== 'muted';
const ports = createMemo(() => formatPorts(app()));
const images = createMemo(() => formatImages(app()));
const volumes = createMemo(() => formatVolumes(app()));
const detailRowId = () => drawer.detailRowId(resource);
const isExpanded = () => drawer.isExpanded(resource);
return (
@@ -365,13 +333,6 @@ export const TrueNASAppsTable: Component<{
{images().label}
</span>
</TableCell>
<TableCell
class={`${getPlatformTableCellClassForKind('numeric-value')} hidden xl:table-cell`}
>
<span class="block truncate" title={volumes().title}>
{volumes().label}
</span>
</TableCell>
<TableCell class={getPlatformTableCellClassForKind('badge')}>
<UpdatePills app={app()} />
</TableCell>
@@ -380,7 +341,7 @@ export const TrueNASAppsTable: Component<{
resource={resource}
open={isExpanded()}
detailRowId={detailRowId()}
colSpan={9}
colSpan={8}
resolveResourceLabel={resolveResourceLabel}
onClose={() => drawer.close(resource)}
/>
@@ -169,34 +169,29 @@ export const TrueNASNetworkSharesTable: Component<{
>
<TableCard class={PLATFORM_TABLE_CARD_CLASS}>
<TableCardHeader title="Network Shares" />
<Table class="min-w-full table-fixed text-xs md:min-w-[1180px]">
<Table class="min-w-full table-fixed text-xs md:min-w-[960px]">
<TableHeader>
<TableRow class={PLATFORM_TABLE_HEADER_ROW_CLASS}>
<TableHead class={`${getPlatformTableHeadClassForKind('name')} md:w-[20%]`}>
<TableHead class={`${getPlatformTableHeadClassForKind('name')} md:w-[23%]`}>
Share
</TableHead>
<TableHead class={`${getPlatformTableHeadClassForKind('text')} md:w-[8%]`}>
<TableHead class={`${getPlatformTableHeadClassForKind('text')} md:w-[11%]`}>
Protocol
</TableHead>
<TableHead
class={`${getPlatformTableHeadClassForKind('text')} hidden sm:table-cell md:w-[15%]`}
>
Dataset
</TableHead>
<TableHead
class={`${getPlatformTableHeadClassForKind('text')} hidden md:table-cell md:w-[23%]`}
class={`${getPlatformTableHeadClassForKind('text')} hidden md:table-cell md:w-[27%]`}
>
Path
</TableHead>
<TableHead class={`${getPlatformTableHeadClassForKind('text')} md:w-[16%]`}>
<TableHead class={`${getPlatformTableHeadClassForKind('text')} md:w-[19%]`}>
Access
</TableHead>
<TableHead
class={`${getPlatformTableHeadClassForKind('text')} hidden lg:table-cell md:w-[12%]`}
class={`${getPlatformTableHeadClassForKind('text')} hidden lg:table-cell md:w-[13%]`}
>
Clients
</TableHead>
<TableHead class={`${getPlatformTableHeadClassForKind('badge')} md:w-[6%]`}>
<TableHead class={`${getPlatformTableHeadClassForKind('badge')} md:w-[7%]`}>
State
</TableHead>
</TableRow>
@@ -249,13 +244,6 @@ export const TrueNASNetworkSharesTable: Component<{
{formatProtocol(share())}
</span>
</TableCell>
<TableCell
class={`${getPlatformTableCellClassForKind('text')} hidden text-base-content sm:table-cell`}
>
<span class="block truncate" title={share()?.dataset}>
{share()?.dataset || '-'}
</span>
</TableCell>
<TableCell
class={`${getPlatformTableCellClassForKind('text')} hidden text-base-content md:table-cell`}
>
@@ -293,7 +281,7 @@ export const TrueNASNetworkSharesTable: Component<{
resource={resource}
open={isExpanded()}
detailRowId={detailRowId()}
colSpan={7}
colSpan={6}
resolveResourceLabel={resolveResourceLabel}
onClose={() => drawer.close(resource)}
/>
@@ -259,21 +259,21 @@ export const TrueNASStorageTopologyTable: Component<{
>
<TableCard class={PLATFORM_TABLE_CARD_CLASS}>
<TableCardHeader title="Storage Topology" />
<Table class="min-w-full table-fixed text-xs md:min-w-[1180px]">
<Table class="min-w-full table-fixed text-xs md:min-w-[960px]">
<TableHeader>
<TableRow class={PLATFORM_TABLE_HEADER_ROW_CLASS}>
<TableHead class={`${getPlatformTableHeadClassForKind('name')} md:w-[28%]`}>
<TableHead class={`${getPlatformTableHeadClassForKind('name')} md:w-[27%]`}>
Resource
</TableHead>
<TableHead class={`${getPlatformTableHeadClassForKind('text')} md:w-[10%]`}>
<TableHead class={`${getPlatformTableHeadClassForKind('text')} md:w-[9%]`}>
Kind
</TableHead>
<TableHead
class={`${getPlatformTableHeadClassForKind('text')} hidden sm:table-cell md:w-[11%]`}
class={`${getPlatformTableHeadClassForKind('text')} hidden sm:table-cell md:w-[10%]`}
>
State
</TableHead>
<TableHead class={`${getPlatformTableHeadClassForKind('metric-bar')} md:w-[21%]`}>
<TableHead class={`${getPlatformTableHeadClassForKind('metric-bar')} md:w-[22%]`}>
Usage / Size
</TableHead>
<TableHead
@@ -287,11 +287,11 @@ export const TrueNASStorageTopologyTable: Component<{
Disks
</TableHead>
<TableHead
class={`${getPlatformTableHeadClassForKind('numeric-value')} hidden lg:table-cell md:w-[7%]`}
class={`${getPlatformTableHeadClassForKind('numeric-value')} hidden lg:table-cell md:w-[6%]`}
>
Temp
</TableHead>
<TableHead class={`${getPlatformTableHeadClassForKind('badge')} md:w-[7%]`}>
<TableHead class={`${getPlatformTableHeadClassForKind('badge')} md:w-[10%]`}>
Health
</TableHead>
</TableRow>
@@ -163,7 +163,7 @@ export const TrueNASVirtualMachinesTable: Component<{
>
<TableCard class={PLATFORM_TABLE_CARD_CLASS}>
<TableCardHeader title="Virtual Machines" />
<Table class="min-w-full table-fixed text-xs md:min-w-[1100px]">
<Table class="min-w-full table-fixed text-xs md:min-w-[960px]">
<TableHeader>
<TableRow class={PLATFORM_TABLE_HEADER_ROW_CLASS}>
<TableHead class={`${getPlatformTableHeadClassForKind('name')} md:w-[22%]`}>
@@ -235,7 +235,10 @@ export const TrueNASVirtualMachinesTable: Component<{
{name()}
</div>
<div class="truncate text-[10px] text-muted">
{vm()?.description || vm()?.uuid || resource.parentName || 'TrueNAS'}
{vm()?.description ||
vm()?.uuid ||
resource.parentName ||
'TrueNAS'}
</div>
</div>
</div>