fix(ui): consistent attribute badge colors based on value hash across all views
All checks were successful
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 59s
CI / docker (push) Successful in 55s
CI / deploy-feature (push) Has been skipped
CI / deploy (push) Successful in 35s

This commit is contained in:
hsiegeln
2026-03-28 15:37:49 +01:00
parent 626501cb04
commit 01c6d5c131
4 changed files with 31 additions and 5 deletions

View File

@@ -1,4 +1,6 @@
import { Badge } from '@cameleer/design-system';
import type { ProcessorNode, ExecutionDetail } from '../types';
import { attributeBadgeColor } from '../../../utils/attribute-color';
import styles from '../ExecutionDiagram.module.css';
interface InfoTabProps {
@@ -52,9 +54,7 @@ function Attributes({ attrs }: { attrs: Record<string, string> | undefined }) {
<div className={styles.attributesLabel}>Attributes</div>
<div className={styles.attributesList}>
{entries.map(([k, v]) => (
<span key={k} className={styles.attributePill}>
{k}: {v}
</span>
<Badge key={k} label={`${k}: ${v}`} color={attributeBadgeColor(String(v))} />
))}
</div>
</div>

View File

@@ -13,6 +13,7 @@ import {
useSearchExecutions,
} from '../../api/queries/executions'
import type { ExecutionSummary } from '../../api/types'
import { attributeBadgeColor } from '../../utils/attribute-color'
import styles from './Dashboard.module.css'
// Row type extends ExecutionSummary with an `id` field for DataTable
@@ -127,7 +128,7 @@ function buildBaseColumns(): Column<Row>[] {
<div className={styles.attrCell}>
{shown.map(([k, v]) => (
<span key={k} title={k}>
<Badge label={String(v)} color="auto" />
<Badge label={String(v)} color={attributeBadgeColor(String(v))} />
</span>
))}
{overflow > 0 && <span className={styles.attrOverflow}>+{overflow}</span>}

View File

@@ -5,6 +5,7 @@ import { StatusDot, MonoText, Badge } from '@cameleer/design-system';
import { useCorrelationChain } from '../../api/queries/correlation';
import { useAgents } from '../../api/queries/agents';
import type { ExecutionDetail } from '../../components/ExecutionDiagram/types';
import { attributeBadgeColor } from '../../utils/attribute-color';
import styles from './ExchangeHeader.module.css';
interface ExchangeHeaderProps {
@@ -63,7 +64,7 @@ export function ExchangeHeader({ detail, onCorrelatedSelect }: ExchangeHeaderPro
<>
<span className={styles.separator} />
{attrs.map(([k, v]) => (
<Badge key={k} label={`${k}: ${v}`} color="auto" />
<Badge key={k} label={`${k}: ${v}`} color={attributeBadgeColor(String(v))} />
))}
</>
)}

View File

@@ -0,0 +1,24 @@
import type { ReactNode } from 'react';
/**
* Deterministic color for attribute badges.
* Uses the value (not the key) to compute the color,
* ensuring the same value always gets the same color
* regardless of where it's displayed.
*
* Returns a BadgeColor that the design system accepts.
*/
const COLORS = ['primary', 'success', 'warning', 'running'] as const;
type BadgeColor = 'primary' | 'success' | 'warning' | 'error' | 'running' | 'auto';
function hashString(s: string): number {
let hash = 0;
for (let i = 0; i < s.length; i++) {
hash = ((hash << 5) - hash + s.charCodeAt(i)) | 0;
}
return Math.abs(hash);
}
export function attributeBadgeColor(value: string): BadgeColor {
return COLORS[hashString(value) % COLORS.length];
}