From 557c5151f2a523174bc7285577b0dd81333541c4 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Wed, 20 May 2026 21:27:40 +0100 Subject: [PATCH] Share app-container column config between Docker and TrueNAS TrueNAS apps are Docker containers running under the embedded daemon, so the apps table on the TrueNAS overview should default to the same column set as the Docker / Podman containers table: writable layer hidden by default, tags hidden by default, and Net I/O and Disk I/O hidden when no container in the set reports telemetry. Previously the TrueNAS surface rendered the workload table with raw defaults, which exposed an empty "Disk" column and an always-visible "Disk I/O" column that no real TrueNAS app populates. Extract APP_CONTAINER_BASE_DEFAULT_HIDDEN_COLUMNS, APP_CONTAINER_COLUMN_LABEL_OVERRIDES, and the telemetry-aware buildAppContainerDefaultHiddenColumnIds into a shared appContainerColumns module under features/platformPage. The Docker page keeps its context: 'Host' relabel on top of the shared overrides; the TrueNAS page now scopes its column-visibility prefs under truenas-app-containers and pulls the same hidden-column defaults from its own app list. buildDockerContainerDefaultHiddenColumnIds is preserved as an alias so the existing docker page tests still pass. --- .../src/features/docker/DockerPageSurface.tsx | 3 +- .../src/features/docker/dockerPageModel.ts | 25 ++-------------- .../platformPage/appContainerColumns.ts | 29 +++++++++++++++++++ .../features/truenas/TrueNASPageSurface.tsx | 10 +++++++ 4 files changed, 43 insertions(+), 24 deletions(-) create mode 100644 frontend-modern/src/features/platformPage/appContainerColumns.ts diff --git a/frontend-modern/src/features/docker/DockerPageSurface.tsx b/frontend-modern/src/features/docker/DockerPageSurface.tsx index 0b544a57df..f4bc70e2c5 100644 --- a/frontend-modern/src/features/docker/DockerPageSurface.tsx +++ b/frontend-modern/src/features/docker/DockerPageSurface.tsx @@ -11,6 +11,7 @@ import { PlatformTableEmptyState, PlatformTableLoadingState, } from '@/features/platformPage/sharedPlatformPage'; +import { APP_CONTAINER_COLUMN_LABEL_OVERRIDES } from '@/features/platformPage/appContainerColumns'; import { DockerHostsTable } from './DockerHostsTable'; import { DockerServicesTable } from './DockerServicesTable'; import { @@ -27,8 +28,8 @@ const DOCKER_WORKLOAD_FORCED_VIEW_MODE = 'app-container'; const DOCKER_WORKLOAD_DEFAULT_SORT_KEY = 'name'; const DOCKER_WORKLOAD_COLUMN_SCOPE = 'docker-runtime-containers'; const DOCKER_WORKLOAD_COLUMN_LABEL_OVERRIDES = { + ...APP_CONTAINER_COLUMN_LABEL_OVERRIDES, context: 'Host', - disk: 'Writable layer', } as const; const DOCKER_WORKLOAD_STATUS_OPTIONS: readonly WorkloadsStatusOption[] = [ { value: 'all', label: 'All' }, diff --git a/frontend-modern/src/features/docker/dockerPageModel.ts b/frontend-modern/src/features/docker/dockerPageModel.ts index 012dda8be1..1b5f989d31 100644 --- a/frontend-modern/src/features/docker/dockerPageModel.ts +++ b/frontend-modern/src/features/docker/dockerPageModel.ts @@ -6,6 +6,7 @@ import { type ResourceBadge, } from '@/utils/resourceBadgePresentation'; import { DEGRADED_HEALTH_STATUSES, OFFLINE_HEALTH_STATUSES } from '@/utils/status'; +import { buildAppContainerDefaultHiddenColumnIds } from '@/features/platformPage/appContainerColumns'; const DOCKER_HOST_TYPES = new Set(['agent', 'docker-host']); const DOCKER_CONTAINER_TYPES = new Set(['app-container']); @@ -54,11 +55,6 @@ export interface DockerPageFilters { statusMode?: WorkloadsStatusMode; } -const DOCKER_CONTAINER_BASE_DEFAULT_HIDDEN_COLUMNS = ['disk', 'tags'] as const; - -const hasFiniteMetric = (value: unknown): value is number => - typeof value === 'number' && Number.isFinite(value); - const RUNTIME_ONLY_SYSTEM_LABELS = new Set(['docker', 'docker / podman', 'podman']); const addDockerGroupBadgeAlias = ( @@ -109,24 +105,7 @@ export function buildDockerWorkloadGroupLabelBadges( return badges; } -export function buildDockerContainerDefaultHiddenColumnIds( - containers: readonly Resource[], -): string[] { - const hasNetworkIOTelemetry = containers.some( - (container) => - hasFiniteMetric(container.network?.rxBytes) || hasFiniteMetric(container.network?.txBytes), - ); - const hasDiskIOTelemetry = containers.some( - (container) => - hasFiniteMetric(container.diskIO?.readRate) || hasFiniteMetric(container.diskIO?.writeRate), - ); - - return [ - ...DOCKER_CONTAINER_BASE_DEFAULT_HIDDEN_COLUMNS, - ...(hasNetworkIOTelemetry ? [] : ['netIo']), - ...(hasDiskIOTelemetry ? [] : ['diskIo']), - ]; -} +export const buildDockerContainerDefaultHiddenColumnIds = buildAppContainerDefaultHiddenColumnIds; export function buildDockerPageModel(resources: Resource[]): DockerPageModel { const dockerResources = resources.filter( diff --git a/frontend-modern/src/features/platformPage/appContainerColumns.ts b/frontend-modern/src/features/platformPage/appContainerColumns.ts new file mode 100644 index 0000000000..6df63fb74e --- /dev/null +++ b/frontend-modern/src/features/platformPage/appContainerColumns.ts @@ -0,0 +1,29 @@ +import type { Resource } from '@/types/resource'; + +export const APP_CONTAINER_BASE_DEFAULT_HIDDEN_COLUMNS = ['disk', 'tags'] as const; + +export const APP_CONTAINER_COLUMN_LABEL_OVERRIDES = { + disk: 'Writable layer', +} as const; + +const hasFiniteMetric = (value: unknown): value is number => + typeof value === 'number' && Number.isFinite(value); + +export function buildAppContainerDefaultHiddenColumnIds( + containers: readonly Resource[], +): string[] { + const hasNetworkIOTelemetry = containers.some( + (container) => + hasFiniteMetric(container.network?.rxBytes) || hasFiniteMetric(container.network?.txBytes), + ); + const hasDiskIOTelemetry = containers.some( + (container) => + hasFiniteMetric(container.diskIO?.readRate) || hasFiniteMetric(container.diskIO?.writeRate), + ); + + return [ + ...APP_CONTAINER_BASE_DEFAULT_HIDDEN_COLUMNS, + ...(hasNetworkIOTelemetry ? [] : ['netIo']), + ...(hasDiskIOTelemetry ? [] : ['diskIo']), + ]; +} diff --git a/frontend-modern/src/features/truenas/TrueNASPageSurface.tsx b/frontend-modern/src/features/truenas/TrueNASPageSurface.tsx index 532a4f1482..a352b4b997 100644 --- a/frontend-modern/src/features/truenas/TrueNASPageSurface.tsx +++ b/frontend-modern/src/features/truenas/TrueNASPageSurface.tsx @@ -14,6 +14,10 @@ import { PlatformTableEmptyState, PlatformTableLoadingState, } from '@/features/platformPage/sharedPlatformPage'; +import { + APP_CONTAINER_COLUMN_LABEL_OVERRIDES, + buildAppContainerDefaultHiddenColumnIds, +} from '@/features/platformPage/appContainerColumns'; import { TrueNASSystemsTable } from './TrueNASSystemsTable'; import { TRUENAS_TAB_SPECS, @@ -116,6 +120,9 @@ interface TrueNASOverviewProps { } function TrueNASOverview(props: TrueNASOverviewProps) { + const truenasAppDefaultHiddenColumns = createMemo(() => + buildAppContainerDefaultHiddenColumnIds(props.model().apps), + ); const workloadsState = useWorkloadsState({ vms: [], containers: [], @@ -128,6 +135,9 @@ function TrueNASOverview(props: TrueNASOverviewProps) { showFilterToolbar: true, suppressPlatformFilter: true, allowEmbeddedScopeFilters: true, + columnVisibilityStorageScope: 'truenas-app-containers', + additionalDefaultHiddenColumnIds: truenasAppDefaultHiddenColumns(), + columnLabelOverrides: APP_CONTAINER_COLUMN_LABEL_OVERRIDES, compactGroupHeaders: true, }); const showSharedFilterToolbar = createMemo(