diff --git a/ui/src/components/ExecutionDiagram/tabs/InfoTab.tsx b/ui/src/components/ExecutionDiagram/tabs/InfoTab.tsx index 65f59566..2facbdc4 100644 --- a/ui/src/components/ExecutionDiagram/tabs/InfoTab.tsx +++ b/ui/src/components/ExecutionDiagram/tabs/InfoTab.tsx @@ -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 | undefined }) {
Attributes
{entries.map(([k, v]) => ( - - {k}: {v} - + ))}
diff --git a/ui/src/pages/Dashboard/Dashboard.tsx b/ui/src/pages/Dashboard/Dashboard.tsx index 6ace5913..d02caa2d 100644 --- a/ui/src/pages/Dashboard/Dashboard.tsx +++ b/ui/src/pages/Dashboard/Dashboard.tsx @@ -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[] {
{shown.map(([k, v]) => ( - + ))} {overflow > 0 && +{overflow}} diff --git a/ui/src/pages/Exchanges/ExchangeHeader.tsx b/ui/src/pages/Exchanges/ExchangeHeader.tsx index b0316ee7..cee38a37 100644 --- a/ui/src/pages/Exchanges/ExchangeHeader.tsx +++ b/ui/src/pages/Exchanges/ExchangeHeader.tsx @@ -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 <> {attrs.map(([k, v]) => ( - + ))} )} diff --git a/ui/src/utils/attribute-color.ts b/ui/src/utils/attribute-color.ts new file mode 100644 index 00000000..ff0bec8c --- /dev/null +++ b/ui/src/utils/attribute-color.ts @@ -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]; +}