mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-12 15:28:12 +00:00
feat(backups): add column visibility toggle with Comment column
- Add useColumnVisibility hook to Backups table - Add ColumnPicker component for toggling columns - Add Comment column for PBS backups (hidden by default) - Owner, Size, Storage, Verified columns now toggleable - Column visibility persisted to localStorage PBS backups with comments now show the comment when the column is enabled via the Columns picker.
This commit is contained in:
@@ -11,9 +11,11 @@ import { Card } from '@/components/shared/Card';
|
||||
import { EmptyState } from '@/components/shared/EmptyState';
|
||||
import { SectionHeader } from '@/components/shared/SectionHeader';
|
||||
import { showTooltip, hideTooltip } from '@/components/shared/Tooltip';
|
||||
import { ColumnPicker } from '@/components/shared/ColumnPicker';
|
||||
import type { BackupType, GuestType, UnifiedBackup } from '@/types/backups';
|
||||
import { usePersistentSignal } from '@/hooks/usePersistentSignal';
|
||||
import { useDebouncedValue } from '@/hooks/useDebouncedValue';
|
||||
import { useColumnVisibility, type ColumnDef } from '@/hooks/useColumnVisibility';
|
||||
import { logger } from '@/utils/logger';
|
||||
|
||||
type BackupSortKey = keyof Pick<
|
||||
@@ -162,6 +164,31 @@ const UnifiedBackups: Component = () => {
|
||||
false, // Default to absolute time
|
||||
);
|
||||
|
||||
// Column visibility definitions for the backup table
|
||||
const BACKUP_COLUMNS: ColumnDef[] = [
|
||||
{ id: 'vmid', label: 'VMID/ID', priority: 'essential' },
|
||||
{ id: 'type', label: 'Type', priority: 'essential' },
|
||||
{ id: 'name', label: 'Name', priority: 'essential' },
|
||||
{ id: 'node', label: 'Node', priority: 'essential' },
|
||||
{ id: 'owner', label: 'Owner', priority: 'secondary', toggleable: true },
|
||||
{ id: 'backupTime', label: 'Time', priority: 'essential' },
|
||||
{ id: 'size', label: 'Size', priority: 'secondary', toggleable: true },
|
||||
{ id: 'backupType', label: 'Backup Type', priority: 'essential' },
|
||||
{ id: 'storage', label: 'Location', priority: 'secondary', toggleable: true },
|
||||
{ id: 'verified', label: 'Verified', priority: 'secondary', toggleable: true },
|
||||
{ id: 'comment', label: 'Comment', priority: 'supplementary', toggleable: true },
|
||||
{ id: 'details', label: 'Details', priority: 'essential' },
|
||||
];
|
||||
|
||||
// Column visibility with persistence - hide less useful columns by default
|
||||
const columnVisibility = useColumnVisibility(
|
||||
STORAGE_KEYS.BACKUPS_HIDDEN_COLUMNS,
|
||||
BACKUP_COLUMNS,
|
||||
['comment'] // Comment hidden by default (often empty)
|
||||
);
|
||||
const visibleColumns = columnVisibility.visibleColumns;
|
||||
const isColumnVisible = (id: string) => visibleColumns().some(col => col.id === id);
|
||||
|
||||
// Helper functions
|
||||
const getDaySuffix = (day: number) => {
|
||||
if (day >= 11 && day <= 13) return 'th';
|
||||
@@ -456,6 +483,7 @@ const UnifiedBackups: Component = () => {
|
||||
protected: backup.protected || false,
|
||||
encrypted: isEncrypted,
|
||||
owner: backup.owner,
|
||||
comment: backup.comment,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2003,6 +2031,16 @@ const UnifiedBackups: Component = () => {
|
||||
setUseRelativeTime={setUseRelativeTime}
|
||||
/>
|
||||
|
||||
{/* Column Visibility Toggle */}
|
||||
<div class="flex justify-end mb-2 -mt-2">
|
||||
<ColumnPicker
|
||||
columns={columnVisibility.availableToggles()}
|
||||
isHidden={columnVisibility.isHiddenByUser}
|
||||
onToggle={columnVisibility.toggle}
|
||||
onReset={columnVisibility.resetToDefaults}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Table */}
|
||||
<Card padding="none" tone="glass" class="mb-4 overflow-hidden">
|
||||
<div class="overflow-x-auto" style="scrollbar-width: none; -ms-overflow-style: none;">
|
||||
@@ -2107,7 +2145,7 @@ const UnifiedBackups: Component = () => {
|
||||
>
|
||||
Node {sortKey() === 'node' && (sortDirection() === 'asc' ? '▲' : '▼')}
|
||||
</th>
|
||||
<Show when={backupTypeFilter() === 'all' || backupTypeFilter() === 'remote'}>
|
||||
<Show when={isColumnVisible('owner') && (backupTypeFilter() === 'all' || backupTypeFilter() === 'remote')}>
|
||||
<th
|
||||
class="px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-600"
|
||||
onClick={() => handleSort('owner')}
|
||||
@@ -2121,7 +2159,7 @@ const UnifiedBackups: Component = () => {
|
||||
>
|
||||
Time {sortKey() === 'backupTime' && (sortDirection() === 'asc' ? '▲' : '▼')}
|
||||
</th>
|
||||
<Show when={backupTypeFilter() !== 'snapshot'}>
|
||||
<Show when={isColumnVisible('size') && backupTypeFilter() !== 'snapshot'}>
|
||||
<th
|
||||
class="px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-600"
|
||||
onClick={() => handleSort('size')}
|
||||
@@ -2136,7 +2174,7 @@ const UnifiedBackups: Component = () => {
|
||||
Backup{' '}
|
||||
{sortKey() === 'backupType' && (sortDirection() === 'asc' ? '▲' : '▼')}
|
||||
</th>
|
||||
<Show when={backupTypeFilter() !== 'snapshot'}>
|
||||
<Show when={isColumnVisible('storage') && backupTypeFilter() !== 'snapshot'}>
|
||||
<th
|
||||
class="px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-600"
|
||||
onClick={() => handleSort('storage')}
|
||||
@@ -2145,7 +2183,7 @@ const UnifiedBackups: Component = () => {
|
||||
{sortKey() === 'storage' && (sortDirection() === 'asc' ? '▲' : '▼')}
|
||||
</th>
|
||||
</Show>
|
||||
<Show when={backupTypeFilter() === 'all' || backupTypeFilter() === 'remote'}>
|
||||
<Show when={isColumnVisible('verified') && (backupTypeFilter() === 'all' || backupTypeFilter() === 'remote')}>
|
||||
<th
|
||||
class="px-2 py-1.5 text-center text-[11px] sm:text-xs font-medium uppercase tracking-wider cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-600"
|
||||
onClick={() => handleSort('verified')}
|
||||
@@ -2154,6 +2192,11 @@ const UnifiedBackups: Component = () => {
|
||||
{sortKey() === 'verified' && (sortDirection() === 'asc' ? ' ▲' : ' ▼')}
|
||||
</th>
|
||||
</Show>
|
||||
<Show when={isColumnVisible('comment') && (backupTypeFilter() === 'all' || backupTypeFilter() === 'remote')}>
|
||||
<th class="px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider">
|
||||
Comment
|
||||
</th>
|
||||
</Show>
|
||||
<th
|
||||
class="px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider"
|
||||
>
|
||||
@@ -2202,7 +2245,7 @@ const UnifiedBackups: Component = () => {
|
||||
<td class="p-0.5 px-1.5 text-sm align-middle">{item.node}</td>
|
||||
<Show
|
||||
when={
|
||||
backupTypeFilter() === 'all' || backupTypeFilter() === 'remote'
|
||||
isColumnVisible('owner') && (backupTypeFilter() === 'all' || backupTypeFilter() === 'remote')
|
||||
}
|
||||
>
|
||||
<td class="p-0.5 px-1.5 text-xs align-middle text-gray-500 dark:text-gray-400">
|
||||
@@ -2214,7 +2257,7 @@ const UnifiedBackups: Component = () => {
|
||||
>
|
||||
{formatTime(item.backupTime * 1000)}
|
||||
</td>
|
||||
<Show when={backupTypeFilter() !== 'snapshot'}>
|
||||
<Show when={isColumnVisible('size') && backupTypeFilter() !== 'snapshot'}>
|
||||
<td
|
||||
class={`p-0.5 px-1.5 align-middle ${getSizeColor(item.size)}`}
|
||||
>
|
||||
@@ -2286,7 +2329,7 @@ const UnifiedBackups: Component = () => {
|
||||
</Show>
|
||||
</div>
|
||||
</td>
|
||||
<Show when={backupTypeFilter() !== 'snapshot'}>
|
||||
<Show when={isColumnVisible('storage') && backupTypeFilter() !== 'snapshot'}>
|
||||
<td class="p-0.5 px-1.5 text-sm align-middle">
|
||||
{item.storage ||
|
||||
(item.datastore &&
|
||||
@@ -2298,7 +2341,7 @@ const UnifiedBackups: Component = () => {
|
||||
</Show>
|
||||
<Show
|
||||
when={
|
||||
backupTypeFilter() === 'all' || backupTypeFilter() === 'remote'
|
||||
isColumnVisible('verified') && (backupTypeFilter() === 'all' || backupTypeFilter() === 'remote')
|
||||
}
|
||||
>
|
||||
<td class="p-0.5 px-1.5 text-center align-middle">
|
||||
@@ -2346,6 +2389,15 @@ const UnifiedBackups: Component = () => {
|
||||
)}
|
||||
</td>
|
||||
</Show>
|
||||
<Show
|
||||
when={
|
||||
isColumnVisible('comment') && (backupTypeFilter() === 'all' || backupTypeFilter() === 'remote')
|
||||
}
|
||||
>
|
||||
<td class="p-0.5 px-1.5 text-xs align-middle text-gray-500 dark:text-gray-400 max-w-[150px] truncate" title={item.comment || ''}>
|
||||
{item.comment || '-'}
|
||||
</td>
|
||||
</Show>
|
||||
<td
|
||||
class="p-0.5 px-1.5 align-middle"
|
||||
onMouseEnter={(e) => {
|
||||
|
||||
@@ -20,4 +20,5 @@ export interface UnifiedBackup {
|
||||
protected: boolean;
|
||||
encrypted?: boolean;
|
||||
owner?: string;
|
||||
comment?: string; // PBS backup comment (only available from direct PBS)
|
||||
}
|
||||
|
||||
@@ -114,6 +114,7 @@ export const STORAGE_KEYS = {
|
||||
DASHBOARD_HIDDEN_COLUMNS: 'dashboardHiddenColumns',
|
||||
HOSTS_HIDDEN_COLUMNS: 'hostsHiddenColumns',
|
||||
DOCKER_HIDDEN_COLUMNS: 'dockerHiddenColumns',
|
||||
BACKUPS_HIDDEN_COLUMNS: 'backupsHiddenColumns',
|
||||
|
||||
// Resources search
|
||||
RESOURCES_SEARCH_HISTORY: 'resourcesSearchHistory',
|
||||
|
||||
Reference in New Issue
Block a user