Normalize alert and status presentation palette

This commit is contained in:
rcourtman 2026-04-23 15:57:03 +01:00
parent b7ec5c77a5
commit 795c16755b
20 changed files with 45 additions and 47 deletions

View file

@ -287,7 +287,7 @@ export function AlertResourceTableRow(props: AlertResourceTableRowProps) {
</div>
</Show>
<Show when={props.resource.type === 'storage' && props.resource.node}>
<span class="rounded px-2 py-0.5 text-[10px] font-medium whitespace-nowrap bg-purple-100 text-purple-700 dark:bg-purple-900 dark:text-purple-300">
<span class="rounded px-2 py-0.5 text-[10px] font-medium whitespace-nowrap bg-amber-100 text-amber-700 dark:bg-amber-900 dark:text-amber-300">
{props.resource.node}
</span>
</Show>

View file

@ -114,13 +114,12 @@ Please:
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
class={`${baseButtonClass} ${sizeClasses[props.size || 'sm']}
bg-purple-100 dark:bg-purple-950
hover:bg-purple-900
text-purple-600 dark:text-purple-400
hover:text-purple-700 dark:hover:text-purple-300
border border-purple-200 dark:border-purple-700
hover:border-purple-300 dark:hover:border-purple-600
${isLocked() ? 'opacity-60 cursor-not-allowed hover:bg-purple-100 dark:hover:bg-purple-950' : ''}
bg-surface
hover:bg-surface-hover
text-blue-600 dark:text-blue-400
hover:text-blue-700 dark:hover:text-blue-300
border border-border
${isLocked() ? 'opacity-60 cursor-not-allowed hover:bg-surface' : ''}
${props.class || ''}`}
title={
isLocked()
@ -155,14 +154,13 @@ Please:
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
class={`${baseButtonClass} px-2 py-1
bg-purple-100 dark:bg-purple-950
hover:bg-purple-900
text-purple-600 dark:text-purple-400
hover:text-purple-700 dark:hover:text-purple-300
border border-purple-200 dark:border-purple-700
hover:border-purple-300 dark:hover:border-purple-600
bg-surface
hover:bg-surface-hover
text-blue-600 dark:text-blue-400
hover:text-blue-700 dark:hover:text-blue-300
border border-border
gap-1.5
${isLocked() ? 'opacity-60 cursor-not-allowed hover:bg-purple-100 dark:hover:bg-purple-950' : ''}
${isLocked() ? 'opacity-60 cursor-not-allowed hover:bg-surface' : ''}
${props.class || ''}`}
title={
isLocked()
@ -192,12 +190,12 @@ Please:
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
class={`${baseButtonClass} px-3 py-1.5
bg-purple-500
hover:bg-purple-600
bg-blue-600
hover:bg-blue-700
text-white font-medium
shadow-sm hover:shadow-sm
gap-2
${isLocked() ? 'opacity-60 cursor-not-allowed hover:bg-purple-500' : ''}
${isLocked() ? 'opacity-60 cursor-not-allowed hover:bg-blue-600' : ''}
${props.class || ''}`}
title={
isLocked()

View file

@ -66,10 +66,10 @@ export const InvestigationMessages: Component<InvestigationMessagesProps> = (pro
{/* Reasoning content (extended thinking) */}
<Show when={msg.reasoning_content}>
<details class="mb-1">
<summary class="text-[10px] text-purple-600 dark:text-purple-400 cursor-pointer hover:underline">
<summary class="text-[10px] text-muted cursor-pointer hover:text-base-content hover:underline">
Show reasoning
</summary>
<div class="mt-1 text-[10px] text-muted whitespace-pre-wrap break-words border-l-2 border-purple-200 dark:border-purple-800 pl-2">
<div class="mt-1 text-[10px] text-muted whitespace-pre-wrap break-words border-l-2 border-border pl-2">
{msg.reasoning_content}
</div>
</details>

View file

@ -285,7 +285,7 @@ export function RunHistoryEntry(props: RunHistoryEntryProps) {
</span>
</Show>
<Show when={run.guests_checked > 0}>
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium bg-purple-50 text-purple-700 dark:bg-purple-900 dark:text-purple-300">
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium bg-rose-50 text-rose-700 dark:bg-rose-900 dark:text-rose-300">
<MonitorIcon class="w-3 h-3" /> {run.guests_checked} VM
{run.guests_checked !== 1 ? 's' : ''}
</span>

View file

@ -9,7 +9,7 @@ describe('alertHistoryPresentation', () => {
expect(getAlertHistorySourcePresentation('ai')).toEqual({
label: 'Patrol',
className:
'text-[10px] px-1.5 py-0.5 rounded font-medium bg-violet-100 dark:bg-violet-900 text-violet-700 dark:text-violet-300',
'text-[10px] px-1.5 py-0.5 rounded font-medium bg-indigo-100 dark:bg-indigo-900 text-indigo-700 dark:text-indigo-300',
});
expect(getAlertHistorySourcePresentation('alert')).toEqual({

View file

@ -23,7 +23,7 @@ describe('alertTabsPresentation', () => {
it('returns disabled mobile presentation', () => {
expect(getAlertsMobileTabClass({ isActive: false, isDisabled: true })).toBe(
'flex-1 min-w-0 rounded-md px-2 py-1.5 text-[11px] font-medium transition-all sm:px-4 sm:py-2 sm:text-xs cursor-not-allowed bg-surface-alt text-muted',
'flex-shrink-0 whitespace-nowrap rounded-md px-3 py-1.5 text-[11px] font-medium transition-all sm:flex-1 sm:min-w-0 sm:px-4 sm:py-2 sm:text-xs cursor-not-allowed bg-surface-alt text-muted',
);
});

View file

@ -22,7 +22,7 @@ describe('dashboardKpiPresentation', () => {
expect(getDashboardKpiPresentation('infrastructure').cardClassName).toContain(
'border-l-blue-500',
);
expect(getDashboardKpiPresentation('workloads').iconClassName).toContain('violet');
expect(getDashboardKpiPresentation('workloads').iconClassName).toContain('rose');
expect(getDashboardKpiPresentation('storage').iconClassName).toContain('cyan');
expect(getDashboardKpiPresentation('alerts').cardClassName).toContain('border-l-amber-500');
});

View file

@ -97,8 +97,8 @@ describe('canManageOrg', () => {
describe('roleBadgeClass', () => {
it('returns owner badge class', () => {
const result = roleBadgeClass('owner');
expect(result).toContain('bg-purple-100');
expect(result).toContain('text-purple-800');
expect(result).toContain('bg-amber-100');
expect(result).toContain('text-amber-800');
});
it('returns admin badge class', () => {

View file

@ -18,8 +18,8 @@ describe('recoveryArtifactModePresentation', () => {
expect(getRecoveryArtifactModePresentation('remote')).toEqual({
label: 'Remote Copy',
aggregateLabel: 'Remote Copies',
badgeClassName: 'bg-purple-100 text-purple-700 dark:bg-purple-900 dark:text-purple-300',
segmentClassName: 'bg-violet-500',
badgeClassName: 'bg-indigo-100 text-indigo-700 dark:bg-indigo-900 dark:text-indigo-300',
segmentClassName: 'bg-indigo-500',
});
});
});

View file

@ -14,9 +14,9 @@ describe('getRecoveryFilterChipPresentation', () => {
it('returns the canonical namespace chip presentation', () => {
expect(getRecoveryFilterChipPresentation('namespace')).toMatchObject({
clearButtonClass:
'rounded px-1 py-0.5 text-[10px] hover:bg-violet-100 dark:hover:bg-violet-900',
'rounded px-1 py-0.5 text-[10px] hover:bg-indigo-100 dark:hover:bg-indigo-900',
label: 'Namespace / Group',
});
expect(getRecoveryFilterChipPresentation('namespace').className).toContain('border-violet-200');
expect(getRecoveryFilterChipPresentation('namespace').className).toContain('border-indigo-200');
});
});

View file

@ -9,7 +9,7 @@ export function getAlertHistorySourcePresentation(source?: string | null): Alert
return {
label: 'Patrol',
className:
'text-[10px] px-1.5 py-0.5 rounded font-medium bg-violet-100 dark:bg-violet-900 text-violet-700 dark:text-violet-300',
'text-[10px] px-1.5 py-0.5 rounded font-medium bg-indigo-100 dark:bg-indigo-900 text-indigo-700 dark:text-indigo-300',
};
}

View file

@ -80,7 +80,7 @@ export function getAlertsMobileTabClass({
: isActive
? 'bg-surface text-base-content shadow-sm'
: 'text-muted hover:text-base-content';
return `flex-1 min-w-0 rounded-md px-2 py-1.5 text-[11px] font-medium transition-all sm:px-4 sm:py-2 sm:text-xs ${tone}`;
return `flex-shrink-0 whitespace-nowrap rounded-md px-3 py-1.5 text-[11px] font-medium transition-all sm:flex-1 sm:min-w-0 sm:px-4 sm:py-2 sm:text-xs ${tone}`;
}
export function getAlertsTabTitle({

View file

@ -26,8 +26,8 @@ const DASHBOARD_KPI_PRESENTATION: Record<DashboardKpiKey, DashboardKpiPresentati
label: 'Workloads',
supportingText: 'VMs, containers, and pods',
cardClassName:
'h-full border-l-[3px] border-l-violet-500 dark:border-l-violet-400 bg-surface group-hover:bg-surface-hover transition-colors',
iconClassName: 'w-3.5 h-3.5 text-violet-500/50 dark:text-violet-400/50',
'h-full border-l-[3px] border-l-rose-500 dark:border-l-rose-400 bg-surface group-hover:bg-surface-hover transition-colors',
iconClassName: 'w-3.5 h-3.5 text-rose-500/50 dark:text-rose-400/50',
icon: ContainerIcon,
},
storage: {

View file

@ -3,7 +3,7 @@ import type { Organization, OrganizationRole } from '@/api/orgs';
const defaultBadgeClass = 'bg-slate-100 text-slate-700';
const roleBadgeClasses: Record<OrganizationRole, string> = {
owner: 'bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200',
owner: 'bg-amber-100 text-amber-800 dark:bg-amber-900 dark:text-amber-200',
admin: 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200',
editor: 'bg-emerald-100 text-emerald-800 dark:bg-emerald-900 dark:text-emerald-200',
viewer: defaultBadgeClass,

View file

@ -22,8 +22,8 @@ export function getRecoveryArtifactModePresentation(
return {
label: 'Remote Copy',
aggregateLabel: 'Remote Copies',
badgeClassName: 'bg-purple-100 text-purple-700 dark:bg-purple-900 dark:text-purple-300',
segmentClassName: 'bg-violet-500',
badgeClassName: 'bg-indigo-100 text-indigo-700 dark:bg-indigo-900 dark:text-indigo-300',
segmentClassName: 'bg-indigo-500',
};
default:
return {

View file

@ -34,13 +34,13 @@ const CHIP_PRESENTATION: Record<RecoveryFilterChipKind, RecoveryFilterChipPresen
label: 'Focused Item',
},
'item-type': {
clearButtonClass: `${CLEAR_BUTTON_BASE_CLASS} hover:bg-fuchsia-100 dark:hover:bg-fuchsia-900`,
className: `${CHIP_BASE_CLASS} border-fuchsia-200 bg-fuchsia-50 text-fuchsia-700 dark:border-fuchsia-700 dark:bg-fuchsia-900 dark:text-fuchsia-200`,
clearButtonClass: `${CLEAR_BUTTON_BASE_CLASS} hover:bg-rose-100 dark:hover:bg-rose-900`,
className: `${CHIP_BASE_CLASS} border-rose-200 bg-rose-50 text-rose-700 dark:border-rose-700 dark:bg-rose-900 dark:text-rose-200`,
label: 'Item Type',
},
namespace: {
clearButtonClass: `${CLEAR_BUTTON_BASE_CLASS} hover:bg-violet-100 dark:hover:bg-violet-900`,
className: `${CHIP_BASE_CLASS} border-violet-200 bg-violet-50 text-violet-700 dark:border-violet-700 dark:bg-violet-900 dark:text-violet-200`,
clearButtonClass: `${CLEAR_BUTTON_BASE_CLASS} hover:bg-indigo-100 dark:hover:bg-indigo-900`,
className: `${CHIP_BASE_CLASS} border-indigo-200 bg-indigo-50 text-indigo-700 dark:border-indigo-700 dark:bg-indigo-900 dark:text-indigo-200`,
label: getRecoveryLocationFacetLabel('namespace'),
},
node: {

View file

@ -122,12 +122,12 @@ const RESOURCE_CHANGE_SOURCE_TYPE_PRESENTATIONS: Record<
pulse_diff: {
label: 'Pulse diff',
plural: 'Pulse diffs',
className: 'bg-violet-100 text-violet-700 dark:bg-violet-900 dark:text-violet-300',
className: 'bg-indigo-100 text-indigo-700 dark:bg-indigo-900 dark:text-indigo-300',
},
heuristic: {
label: 'Heuristic',
plural: 'Heuristics',
className: 'bg-fuchsia-100 text-fuchsia-700 dark:bg-fuchsia-900 dark:text-fuchsia-300',
className: 'bg-rose-100 text-rose-700 dark:bg-rose-900 dark:text-rose-300',
},
user_action: {
label: 'User action',

View file

@ -91,7 +91,7 @@ const routingPresentation: Record<
'local-only': {
label: 'Local Only',
title: 'This resource must remain within the local boundary.',
className: `${badgeBaseClass} bg-violet-100 text-violet-700 dark:bg-violet-900 dark:text-violet-300`,
className: `${badgeBaseClass} bg-teal-100 text-teal-700 dark:bg-teal-900 dark:text-teal-300`,
},
};

View file

@ -16,7 +16,7 @@ const SOURCE_TYPE_PRESENTATION: Record<SourceType, SourceTypePresentation> = {
},
hybrid: {
label: 'Hybrid',
badgeClasses: 'bg-purple-100 text-purple-700 dark:bg-purple-900 dark:text-purple-400',
badgeClasses: 'bg-teal-100 text-teal-700 dark:bg-teal-900 dark:text-teal-400',
},
};

View file

@ -7,7 +7,7 @@ export type StorageSourceTone =
| 'orange'
| 'indigo'
| 'rose'
| 'violet'
| 'teal'
| 'cyan'
| 'blue';
@ -27,7 +27,7 @@ const STORAGE_SOURCE_TONES: Record<string, StorageSourceTone> = {
'proxmox-pve': 'orange',
'proxmox-pbs': 'indigo',
'proxmox-pmg': 'rose',
ceph: 'violet',
ceph: 'teal',
truenas: 'blue',
kubernetes: 'cyan',
};