feat(mobile): improve table layouts for remaining pages

- Add horizontal scrolling to Hosts table with 900px min-width
- Add horizontal scrolling to Ceph tables with 700/650px min-widths
- Tighten padding on Storage tables for compact mobile display
- Add min-width to DiskList table for physical disks
- Add min-width to Settings tables (PVE, PBS, PMG, Agents)
- Add horizontal scroll container to Alerts patrol run table
- Hide scrollbars across all scrollable tables for cleaner UI
This commit is contained in:
rcourtman 2025-12-25 23:07:49 +00:00
parent be0d777158
commit 4f4fdcf81b
7 changed files with 61 additions and 58 deletions

View file

@ -748,7 +748,7 @@ export const HostsOverview: Component = () => {
const thClass = "px-2 py-1 text-center text-[11px] sm:text-xs font-medium uppercase tracking-wider cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-600 whitespace-nowrap";
const thClass = "px-1.5 py-1 text-center text-[11px] sm:text-xs font-medium uppercase tracking-wider cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-600 whitespace-nowrap";
return (
<div class="space-y-4">
@ -859,8 +859,9 @@ export const HostsOverview: Component = () => {
}
>
<Card padding="none" tone="glass" class="overflow-hidden">
<div class="overflow-x-auto">
<table class="w-full border-collapse whitespace-nowrap">
<div class="overflow-x-auto" style="scrollbar-width: none; -ms-overflow-style: none;">
<style>{`.overflow-x-auto::-webkit-scrollbar { display: none; }`}</style>
<table class="w-full border-collapse whitespace-nowrap" style={{ "min-width": "900px" }}>
<thead>
<tr class="bg-gray-50 dark:bg-gray-700/50 text-gray-600 dark:text-gray-300 border-b border-gray-200 dark:border-gray-700">
{/* Essential columns */}

View file

@ -292,7 +292,7 @@ const findMatchingHostAgent = (
export const PveNodesTable: Component<PveNodesTableProps> = (props) => {
return (
<Card padding="none" tone="glass" class="overflow-x-auto rounded-lg">
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700 text-sm">
<table class="min-w-[800px] divide-y divide-gray-200 dark:divide-gray-700 text-sm">
<thead class="bg-gray-50 dark:bg-gray-800/70">
<tr>
<th scope="col" class="py-2 pl-4 pr-3 text-left text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">
@ -594,7 +594,7 @@ const resolvePbsStatusMeta = (
export const PbsNodesTable: Component<PbsNodesTableProps> = (props) => {
return (
<Card padding="none" tone="glass" class="overflow-x-auto rounded-lg">
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700 text-sm">
<table class="min-w-[800px] divide-y divide-gray-200 dark:divide-gray-700 text-sm">
<thead class="bg-gray-50 dark:bg-gray-800/70">
<tr>
<th scope="col" class="py-2 pl-4 pr-3 text-left text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">
@ -790,7 +790,7 @@ const resolvePmgStatusMeta = (
export const PmgNodesTable: Component<PmgNodesTableProps> = (props) => {
return (
<Card padding="none" tone="glass" class="overflow-x-auto rounded-lg">
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700 text-sm">
<table class="min-w-[800px] divide-y divide-gray-200 dark:divide-gray-700 text-sm">
<thead class="bg-gray-50 dark:bg-gray-800/70">
<tr>
<th scope="col" class="py-2 pl-4 pr-3 text-left text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">

View file

@ -900,8 +900,8 @@ export const UnifiedAgents: Component = () => {
</div>
</Show>
<Card padding="none" tone="glass" class="overflow-hidden rounded-lg">
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
<Card padding="none" tone="glass" class="overflow-x-auto rounded-lg">
<table class="min-w-[800px] divide-y divide-gray-200 dark:divide-gray-700">
<thead class="bg-gray-50 dark:bg-gray-800">
<tr>
<th scope="col" class="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500 dark:text-gray-400">Hostname</th>
@ -1031,8 +1031,8 @@ export const UnifiedAgents: Component = () => {
</p>
</div>
<Card padding="none" tone="glass" class="overflow-hidden rounded-lg">
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
<Card padding="none" tone="glass" class="overflow-x-auto rounded-lg">
<table class="min-w-[800px] divide-y divide-gray-200 dark:divide-gray-700">
<thead class="bg-gray-50 dark:bg-gray-800">
<tr>
<th scope="col" class="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500 dark:text-gray-400">Cluster</th>
@ -1094,8 +1094,8 @@ export const UnifiedAgents: Component = () => {
</p>
</div>
<Card padding="none" tone="glass" class="overflow-hidden rounded-lg">
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
<Card padding="none" tone="glass" class="overflow-x-auto rounded-lg">
<table class="min-w-[800px] divide-y divide-gray-200 dark:divide-gray-700">
<thead class="bg-gray-50 dark:bg-gray-800">
<tr>
<th scope="col" class="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500 dark:text-gray-400">Hostname</th>
@ -1143,8 +1143,8 @@ export const UnifiedAgents: Component = () => {
</p>
</div>
<Card padding="none" tone="glass" class="overflow-hidden rounded-lg">
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
<Card padding="none" tone="glass" class="overflow-x-auto rounded-lg">
<table class="min-w-[800px] divide-y divide-gray-200 dark:divide-gray-700">
<thead class="bg-gray-50 dark:bg-gray-800">
<tr>
<th scope="col" class="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500 dark:text-gray-400">Cluster</th>

View file

@ -151,37 +151,37 @@ export const DiskList: Component<DiskListProps> = (props) => {
<Show when={filteredDisks().length > 0}>
<Card padding="none" tone="glass" class="overflow-hidden">
<div class="overflow-x-auto">
<table class="w-full">
<table class="w-full" style={{ "min-width": "750px" }}>
<thead>
<tr class="bg-gray-50 dark:bg-gray-700/50 text-gray-600 dark:text-gray-300 border-b border-gray-200 dark:border-gray-600">
<th class="px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[10%]">
<th class="px-1 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[10%]">
Node
</th>
<th class="px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[10%]">
<th class="px-1 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[10%]">
Device
</th>
<th class="hidden md:table-cell px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[20%]">
<th class="px-1 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[20%]">
Model
</th>
<th class="hidden sm:table-cell px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[8%]">
<th class="px-1 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[8%]">
Type
</th>
<th class="hidden lg:table-cell px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[8%]">
<th class="px-1 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[8%]">
FS
</th>
<th class="px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[10%]">
<th class="px-1 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[10%]">
Health
</th>
<th class="hidden md:table-cell px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[15%]">
<th class="px-1 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[15%]">
SSD Life
</th>
<th class="px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider hidden sm:table-cell w-[8%]">
<th class="px-1 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[8%]">
Temp
</th>
<th class="px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[10%]">
<th class="px-1 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[10%]">
Size
</th>
<th class="px-2 py-1.5 w-8"></th>
<th class="px-1 py-1.5 w-8"></th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 dark:divide-gray-700">
@ -192,29 +192,29 @@ export const DiskList: Component<DiskListProps> = (props) => {
return (
<>
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700/30 transition-colors">
<td class="px-2 py-1.5 text-xs">
<td class="px-1 py-1.5 text-xs">
<span class="font-medium text-gray-900 dark:text-gray-100">
{disk.node}
</span>
</td>
<td class="px-2 py-1.5 text-xs">
<td class="px-1 py-1.5 text-xs">
<span class="font-mono text-gray-600 dark:text-gray-400">
{disk.devPath}
</span>
</td>
<td class="hidden md:table-cell px-2 py-1.5 text-xs">
<td class="px-1 py-1.5 text-xs">
<span class="text-gray-700 dark:text-gray-300">
{disk.model || 'Unknown'}
</span>
</td>
<td class="hidden sm:table-cell px-2 py-1.5 text-xs">
<td class="px-1 py-1.5 text-xs">
<span
class={`inline-block px-1.5 py-0.5 text-[10px] font-medium rounded ${getDiskTypeBadge(disk.type)}`}
>
{disk.type.toUpperCase()}
</span>
</td>
<td class="hidden lg:table-cell px-2 py-1.5 text-xs">
<td class="px-1 py-1.5 text-xs">
<Show
when={disk.used && disk.used !== 'unknown'}
fallback={<span class="text-gray-400">-</span>}
@ -224,14 +224,14 @@ export const DiskList: Component<DiskListProps> = (props) => {
</span>
</Show>
</td>
<td class="px-2 py-1.5 text-xs">
<td class="px-1 py-1.5 text-xs">
<span
class={`inline-block px-1.5 py-0.5 text-[10px] font-medium rounded ${health.bgColor} ${health.color}`}
>
{health.text}
</span>
</td>
<td class="hidden md:table-cell px-2 py-1.5 text-xs">
<td class="px-1 py-1.5 text-xs">
<Show
when={disk.wearout > 0}
fallback={<span class="text-gray-400">-</span>}
@ -254,7 +254,7 @@ export const DiskList: Component<DiskListProps> = (props) => {
</div>
</Show>
</td>
<td class="px-2 py-1.5 text-xs hidden sm:table-cell">
<td class="px-1 py-1.5 text-xs">
<Show
when={typeof disk.temperature === 'number' && disk.temperature !== 0}
fallback={<span class="font-medium text-gray-400">-</span>}
@ -271,12 +271,12 @@ export const DiskList: Component<DiskListProps> = (props) => {
</span>
</Show>
</td>
<td class="px-2 py-1.5 text-xs">
<td class="px-1 py-1.5 text-xs">
<span class="text-gray-700 dark:text-gray-300">
{formatBytes(disk.size)}
</span>
</td>
<td class="px-2 py-1.5"></td>
<td class="px-1 py-1.5"></td>
</tr>
</>
);

View file

@ -788,36 +788,36 @@ const Storage: Component = () => {
<style>{`
.overflow-x-auto::-webkit-scrollbar { display: none; }
`}</style>
<table class="w-full" style={{ "min-width": "900px" }}>
<table class="w-full" style={{ "min-width": "750px" }}>
<thead>
<tr class="bg-gray-50 dark:bg-gray-700/50 text-gray-600 dark:text-gray-300 border-b border-gray-200 dark:border-gray-600">
<th class="px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-auto">
<th class="px-1.5 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-auto">
Storage
</th>
<th class="px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[10%]">
<th class="px-1.5 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[10%]">
Type
</th>
<th class="px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[15%]">
<th class="px-1.5 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[15%]">
Content
</th>
<th class="px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[10%]">
<th class="px-1.5 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[10%]">
Status
</th>
<Show when={viewMode() === 'node'}>
<th class="px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[8%]">
<th class="px-1.5 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[6%]">
Shared
</th>
</Show>
<th class="px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[25%] min-w-[150px]">
<th class="px-1.5 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[25%] min-w-[120px]">
Usage
</th>
<th class="px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[10%]">
<th class="px-1.5 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[10%]">
Free
</th>
<th class="px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[10%]">
<th class="px-1.5 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[10%]">
Total
</th>
<th class="px-2 py-1.5 w-8"></th>
<th class="px-1.5 py-1.5 w-8"></th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 dark:divide-gray-700">
@ -1064,12 +1064,12 @@ const Storage: Component = () => {
const firstCellClass = createMemo(() => {
if (viewMode() === 'node') {
return firstCellHasIndicator()
? 'p-0.5 pl-7 pr-1.5'
: 'p-0.5 pl-8 pr-1.5';
? 'p-0.5 pl-6 pr-1.5'
: 'p-0.5 pl-7 pr-1.5';
}
return firstCellHasIndicator()
? 'p-0.5 pl-3 pr-1.5'
: 'p-0.5 pl-3 pr-1.5';
? 'p-0.5 pl-2 pr-1.5'
: 'p-0.5 pl-2 pr-1.5';
});
const toggleDrawer = () => {
@ -1088,9 +1088,9 @@ const Storage: Component = () => {
aria-expanded={canExpand() && isExpanded() ? 'true' : 'false'}
>
<td class={`${firstCellClass()} align-middle`}>
<div class="flex items-center gap-2 min-w-0">
<div class="flex items-center gap-1.5 min-w-0">
<span
class={`text-sm font-medium text-gray-900 dark:text-gray-100 truncate ${canExpand() ? 'max-w-[180px]' : 'max-w-[200px]'
class={`text-xs sm:text-sm font-medium text-gray-900 dark:text-gray-100 truncate ${canExpand() ? 'max-w-[140px] sm:max-w-[180px]' : 'max-w-[160px] sm:max-w-[200px]'
}`}
title={storage.name}
>
@ -1098,7 +1098,7 @@ const Storage: Component = () => {
</span>
{/* ZFS Health Map */}
<Show when={zfsPool && zfsPool.devices && zfsPool.devices.length > 0}>
<div class="mx-1.5">
<div class="mx-1">
<ZFSHealthMap pool={zfsPool!} />
</div>
</Show>

View file

@ -3370,8 +3370,8 @@ function OverviewTab(props: {
<p class="text-sm text-gray-500 dark:text-gray-400 italic py-4">No patrol runs in selected time range.</p>
}
>
<div class="border border-gray-200 dark:border-gray-700 rounded overflow-hidden">
<table class="w-full text-xs sm:text-sm">
<div class="border border-gray-200 dark:border-gray-700 rounded overflow-x-auto">
<table class="w-full min-w-[600px] text-xs sm:text-sm">
<thead>
<tr class="bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 border-b border-gray-300 dark:border-gray-600">
<th class="p-1.5 px-2 text-left text-[10px] sm:text-xs font-medium uppercase tracking-wider w-4"></th>

View file

@ -488,8 +488,9 @@ const Ceph: Component = () => {
Cluster Overview
</h3>
</div>
<div class="overflow-x-auto">
<table class="w-full border-collapse whitespace-nowrap">
<div class="overflow-x-auto" style="scrollbar-width: none; -ms-overflow-style: none;">
<style>{`.overflow-x-auto::-webkit-scrollbar { display: none; }`}</style>
<table class="w-full border-collapse whitespace-nowrap" style={{ "min-width": "700px" }}>
<thead>
<tr class="bg-gray-50 dark:bg-gray-700/50 text-gray-600 dark:text-gray-300 border-b border-gray-200 dark:border-gray-700">
<th class={`${thClass} pl-4`}>Cluster</th>
@ -612,8 +613,9 @@ const Ceph: Component = () => {
</div>
}
>
<div class="overflow-x-auto">
<table class="w-full border-collapse whitespace-nowrap">
<div class="overflow-x-auto" style="scrollbar-width: none; -ms-overflow-style: none;">
<style>{`.overflow-x-auto::-webkit-scrollbar { display: none; }`}</style>
<table class="w-full border-collapse whitespace-nowrap" style={{ "min-width": "650px" }}>
<thead>
<tr class="bg-gray-50 dark:bg-gray-700/50 text-gray-600 dark:text-gray-300 border-b border-gray-200 dark:border-gray-700">
<th class={`${thClass} pl-4`}>Pool</th>