diff --git a/docs/release-control/v6/internal/subsystems/storage-recovery.md b/docs/release-control/v6/internal/subsystems/storage-recovery.md index d361ea4b0..fa620a176 100644 --- a/docs/release-control/v6/internal/subsystems/storage-recovery.md +++ b/docs/release-control/v6/internal/subsystems/storage-recovery.md @@ -223,6 +223,10 @@ That same runtime-helper contract should prefer `item` terminology in shared recovery presenters too. Helper exports that resolve labels or item-type badges should expose canonical item-facing names, while any retained `subject` aliases remain compatibility wrappers instead of the primary runtime boundary. +That same shared badge contract applies to table rendering too. Recovery item +type cells should use the same compact monitoring-table badge base that +workloads uses for `VM` and `Container`, rather than copying only the colors +and drifting on padding or visual weight. The same rule applies inside recovery-owned helpers and selectors. Shared summary helpers and platform filter renderers should use canonical `item` and `platform` naming internally once compatibility boundaries already exist, diff --git a/frontend-modern/src/components/Recovery/RecoveryHistoryTable.tsx b/frontend-modern/src/components/Recovery/RecoveryHistoryTable.tsx index 533b5619b..e310e065a 100644 --- a/frontend-modern/src/components/Recovery/RecoveryHistoryTable.tsx +++ b/frontend-modern/src/components/Recovery/RecoveryHistoryTable.tsx @@ -231,7 +231,7 @@ export const RecoveryHistoryTable: Component = (props when={itemTypePresentation} fallback={} > - + {itemTypePresentation?.label} diff --git a/frontend-modern/src/components/Recovery/RecoveryProtectedInventorySection.tsx b/frontend-modern/src/components/Recovery/RecoveryProtectedInventorySection.tsx index 252882682..44bff36eb 100644 --- a/frontend-modern/src/components/Recovery/RecoveryProtectedInventorySection.tsx +++ b/frontend-modern/src/components/Recovery/RecoveryProtectedInventorySection.tsx @@ -471,7 +471,7 @@ export const RecoveryProtectedInventorySection: Component<
- + {itemTypePresentation?.label} @@ -496,7 +496,7 @@ export const RecoveryProtectedInventorySection: Component< when={itemTypePresentation} fallback={} > - + {itemTypePresentation?.label} diff --git a/frontend-modern/src/components/Recovery/__tests__/Recovery.test.tsx b/frontend-modern/src/components/Recovery/__tests__/Recovery.test.tsx index 4743b2d02..596db9699 100644 --- a/frontend-modern/src/components/Recovery/__tests__/Recovery.test.tsx +++ b/frontend-modern/src/components/Recovery/__tests__/Recovery.test.tsx @@ -270,6 +270,7 @@ describe('Recovery', () => { ); expect(within(vmRow!).getAllByText('VM')[0].className).toContain('bg-'); expect(within(vmRow!).getAllByText('VM')[0].className).toContain('rounded'); + expect(within(vmRow!).getAllByText('VM')[0].className).toContain('px-1 py-0.5'); expect(within(vmRow!).getByText('Success').className).not.toContain('rounded'); expect(screen.queryByText('Backups By Date')).not.toBeInTheDocument(); expect(screen.queryByText('Recovery Activity')).not.toBeInTheDocument(); @@ -351,6 +352,7 @@ describe('Recovery', () => { ); expect(within(historyRow!).getByText('VM').className).toContain('bg-'); expect(within(historyRow!).getByText('VM').className).toContain('rounded'); + expect(within(historyRow!).getByText('VM').className).toContain('px-1 py-0.5'); expect(within(historyRow!).getByText('Local Copy').className).not.toContain('rounded'); expect(within(historyRow!).getByText('Success').className).not.toContain('rounded'); expect(activityHeading).toBeInTheDocument(); diff --git a/frontend-modern/src/utils/__tests__/recoveryItemTypePresentation.test.ts b/frontend-modern/src/utils/__tests__/recoveryItemTypePresentation.test.ts index a46033873..58be2b58a 100644 --- a/frontend-modern/src/utils/__tests__/recoveryItemTypePresentation.test.ts +++ b/frontend-modern/src/utils/__tests__/recoveryItemTypePresentation.test.ts @@ -47,6 +47,7 @@ describe('recoveryItemTypePresentation', () => { expect(getRecoveryItemTypeLabel('custom-thing')).toBe('Custom Thing'); expect(getRecoveryItemTypeBadgeClass('custom-thing')).toContain('bg-surface-alt text-base-content'); expect(getRecoveryItemTypeBadgeClass('custom-thing')).toContain('inline-flex items-center'); + expect(getRecoveryItemTypeBadgeClass('custom-thing')).toContain('px-1 py-0.5'); }); it('derives canonical item type keys from recovery rollups and points', () => { diff --git a/frontend-modern/src/utils/__tests__/recoveryTablePresentation.test.ts b/frontend-modern/src/utils/__tests__/recoveryTablePresentation.test.ts index d913fdec8..42b0b0934 100644 --- a/frontend-modern/src/utils/__tests__/recoveryTablePresentation.test.ts +++ b/frontend-modern/src/utils/__tests__/recoveryTablePresentation.test.ts @@ -80,6 +80,7 @@ describe('recoveryTablePresentation', () => { expect(getRecoveryPointItemTypeLabel(point)).toBe('VM'); expect(getRecoveryPointItemTypeBadgeClass(point)).toContain('bg-blue-100'); expect(getRecoveryPointItemTypeBadgeClass(point)).toContain('text-blue-700'); + expect(getRecoveryPointItemTypeBadgeClass(point)).toContain('px-1 py-0.5'); }); it('normalizes proxmox lxc subjects to the canonical container badge', () => { @@ -91,6 +92,7 @@ describe('recoveryTablePresentation', () => { expect(getRecoveryPointItemTypeLabel(point)).toBe('Container'); expect(getRecoveryPointItemTypeBadgeClass(point)).toContain('bg-green-100'); expect(getRecoveryPointItemTypeBadgeClass(point)).toContain('text-green-700'); + expect(getRecoveryPointItemTypeBadgeClass(point)).toContain('px-1 py-0.5'); }); it('falls back cleanly for unknown subject types', () => { diff --git a/frontend-modern/src/utils/recoveryItemTypePresentation.ts b/frontend-modern/src/utils/recoveryItemTypePresentation.ts index fe654acef..ab708521d 100644 --- a/frontend-modern/src/utils/recoveryItemTypePresentation.ts +++ b/frontend-modern/src/utils/recoveryItemTypePresentation.ts @@ -7,12 +7,16 @@ export interface RecoveryItemTypePresentation { key: string; label: string; badgeClasses: string; + tableBadgeClasses: string; } const BADGE_BASE_CLASSES = 'inline-flex items-center rounded px-2 py-0.5 text-[10px] font-medium whitespace-nowrap'; +const TABLE_BADGE_BASE_CLASSES = + 'inline-flex items-center px-1 py-0.5 text-[10px] font-medium rounded whitespace-nowrap'; const DEFAULT_BADGE_TONE_CLASSES = 'bg-surface-alt text-base-content'; const DEFAULT_BADGE_CLASSES = `${BADGE_BASE_CLASSES} ${DEFAULT_BADGE_TONE_CLASSES}`; +const DEFAULT_TABLE_BADGE_CLASSES = `${TABLE_BADGE_BASE_CLASSES} ${DEFAULT_BADGE_TONE_CLASSES}`; interface RecoveryItemTypeLike { display?: { @@ -94,6 +98,7 @@ export const getRecoveryItemTypePresentation = ( key, label: presentation.label, badgeClasses: `${BADGE_BASE_CLASSES} ${presentation.className}`, + tableBadgeClasses: `${TABLE_BADGE_BASE_CLASSES} ${presentation.className}`, }; } case 'system-container': { @@ -102,6 +107,7 @@ export const getRecoveryItemTypePresentation = ( key, label: presentation.label, badgeClasses: `${BADGE_BASE_CLASSES} ${presentation.className}`, + tableBadgeClasses: `${TABLE_BADGE_BASE_CLASSES} ${presentation.className}`, }; } case 'app-container': { @@ -113,6 +119,7 @@ export const getRecoveryItemTypePresentation = ( key, label: presentation.label, badgeClasses: `${BADGE_BASE_CLASSES} ${presentation.className}`, + tableBadgeClasses: `${TABLE_BADGE_BASE_CLASSES} ${presentation.className}`, }; } case 'oci-container': { @@ -121,6 +128,7 @@ export const getRecoveryItemTypePresentation = ( key, label: presentation.label, badgeClasses: `${BADGE_BASE_CLASSES} ${presentation.className}`, + tableBadgeClasses: `${TABLE_BADGE_BASE_CLASSES} ${presentation.className}`, }; } case 'pod': { @@ -129,6 +137,7 @@ export const getRecoveryItemTypePresentation = ( key, label: presentation.label, badgeClasses: `${BADGE_BASE_CLASSES} ${presentation.className}`, + tableBadgeClasses: `${TABLE_BADGE_BASE_CLASSES} ${presentation.className}`, }; } case 'pvc': { @@ -137,6 +146,7 @@ export const getRecoveryItemTypePresentation = ( key, label: presentation?.label || 'PVC', badgeClasses: `${BADGE_BASE_CLASSES} ${presentation?.badgeClasses || DEFAULT_BADGE_TONE_CLASSES}`, + tableBadgeClasses: `${TABLE_BADGE_BASE_CLASSES} ${presentation?.badgeClasses || DEFAULT_BADGE_TONE_CLASSES}`, }; } default: { @@ -153,6 +163,7 @@ export const getRecoveryItemTypePresentation = ( key, label, badgeClasses: `${BADGE_BASE_CLASSES} ${presentation.badgeClasses}`, + tableBadgeClasses: `${TABLE_BADGE_BASE_CLASSES} ${presentation.badgeClasses}`, }; } return { @@ -162,6 +173,7 @@ export const getRecoveryItemTypePresentation = ( preserveShortAllCaps: true, }), badgeClasses: DEFAULT_BADGE_CLASSES, + tableBadgeClasses: DEFAULT_TABLE_BADGE_CLASSES, }; } } @@ -171,7 +183,7 @@ export const getRecoveryItemTypeLabel = (value: string | null | undefined): stri getRecoveryItemTypePresentation(value)?.label || ''; export const getRecoveryItemTypeBadgeClass = (value: string | null | undefined): string => - getRecoveryItemTypePresentation(value)?.badgeClasses || DEFAULT_BADGE_CLASSES; + getRecoveryItemTypePresentation(value)?.tableBadgeClasses || DEFAULT_TABLE_BADGE_CLASSES; export const getRecoveryRollupItemTypeKey = ( rollup: RecoveryItemTypeLike | null | undefined,