feat: add generic badge system to RouteFlow and ProcessorTimeline
All checks were successful
Build & Publish / publish (push) Successful in 49s

New NodeBadge interface with variant colors (info/success/warning/error)
and optional onClick. Replaces single-purpose bottleneckBadge with a
flexible badges array. Backwards compatible: isBottleneck still works.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-24 23:06:55 +01:00
parent 19dccb8685
commit 63e16d2685
5 changed files with 71 additions and 7 deletions

View File

@@ -129,6 +129,25 @@
color: var(--text-primary);
}
/* Badges */
.badge {
font-family: var(--font-mono);
font-size: 7px;
font-weight: 600;
color: #fff;
border-radius: 6px;
padding: 0 4px;
text-transform: uppercase;
white-space: nowrap;
margin-left: 4px;
vertical-align: middle;
}
.badgeInfo { background: var(--running); }
.badgeSuccess { background: var(--success); }
.badgeWarning { background: var(--amber); }
.badgeError { background: var(--error); }
.empty {
color: var(--text-muted);
font-size: 12px;

View File

@@ -1,6 +1,7 @@
import type { ReactNode } from 'react'
import styles from './ProcessorTimeline.module.css'
import { Dropdown } from '../Dropdown/Dropdown'
import type { NodeBadge } from '../RouteFlow/RouteFlow'
export interface ProcessorStep {
name: string
@@ -8,6 +9,7 @@ export interface ProcessorStep {
durationMs: number
status: 'ok' | 'slow' | 'fail'
startMs: number
badges?: NodeBadge[]
}
export interface ProcessorAction {
@@ -84,6 +86,16 @@ export function ProcessorTimeline({
>
<div className={styles.name} title={proc.name}>
{proc.name}
{proc.badges?.map((badge, bi) => (
<span
key={bi}
className={`${styles.badge} ${styles[`badge${(badge.variant ?? 'info').charAt(0).toUpperCase()}${(badge.variant ?? 'info').slice(1)}`] ?? styles.badgeInfo}`}
onClick={badge.onClick ? (e) => { e.stopPropagation(); badge.onClick!() } : undefined}
style={badge.onClick ? { cursor: 'pointer' } : undefined}
>
{badge.label}
</span>
))}
</div>
<div className={styles.barBg}>
<div

View File

@@ -222,17 +222,29 @@
color: var(--text-primary);
}
/* Bottleneck badge */
.bottleneckBadge {
/* Badges */
.badgeRow {
position: absolute;
top: -7px;
right: 8px;
display: flex;
gap: 3px;
pointer-events: auto;
}
.badge {
font-family: var(--font-mono);
font-size: 8px;
font-weight: 600;
padding: 1px 6px;
border-radius: 8px;
background: var(--error);
color: #fff;
border-radius: 8px;
padding: 1px 6px;
text-transform: uppercase;
white-space: nowrap;
letter-spacing: 0.3px;
}
.badgeInfo { background: var(--running); }
.badgeSuccess { background: var(--success); }
.badgeWarning { background: var(--amber); }
.badgeError { background: var(--error); }

View File

@@ -2,12 +2,19 @@ import type { ReactNode } from 'react'
import styles from './RouteFlow.module.css'
import { Dropdown } from '../Dropdown/Dropdown'
export interface NodeBadge {
label: string
variant?: 'info' | 'success' | 'warning' | 'error'
onClick?: () => void
}
export interface RouteNode {
name: string
type: 'from' | 'process' | 'to' | 'choice' | 'error-handler'
durationMs: number
status: 'ok' | 'slow' | 'fail'
isBottleneck?: boolean
badges?: NodeBadge[]
}
export interface NodeAction {
@@ -132,7 +139,21 @@ export function RouteFlow({ nodes, onNodeClick, selectedIndex, actions, getActio
}
}}
>
{node.isBottleneck && <span className={styles.bottleneckBadge}>BOTTLENECK</span>}
{(node.isBottleneck || node.badges?.length) ? (
<span className={styles.badgeRow}>
{node.isBottleneck && <span className={`${styles.badge} ${styles.badgeError}`}>BOTTLENECK</span>}
{node.badges?.map((badge, bi) => (
<span
key={bi}
className={`${styles.badge} ${styles[`badge${(badge.variant ?? 'info').charAt(0).toUpperCase()}${(badge.variant ?? 'info').slice(1)}`] ?? styles.badgeInfo}`}
onClick={badge.onClick ? (e) => { e.stopPropagation(); badge.onClick!() } : undefined}
style={badge.onClick ? { cursor: 'pointer' } : undefined}
>
{badge.label}
</span>
))}
</span>
) : null}
<div className={`${styles.icon} ${ICON_CLASSES[node.type] ?? styles.iconTo}`}>
{TYPE_ICONS[node.type] ?? '\u25A2'}
</div>

View File

@@ -34,7 +34,7 @@ export { Popover } from './Popover/Popover'
export { ProcessorTimeline } from './ProcessorTimeline/ProcessorTimeline'
export type { ProcessorStep, ProcessorAction } from './ProcessorTimeline/ProcessorTimeline'
export { RouteFlow } from './RouteFlow/RouteFlow'
export type { RouteNode, NodeAction } from './RouteFlow/RouteFlow'
export type { RouteNode, NodeAction, NodeBadge } from './RouteFlow/RouteFlow'
export { ShortcutsBar } from './ShortcutsBar/ShortcutsBar'
export { SegmentedTabs } from './SegmentedTabs/SegmentedTabs'
export { SplitPane } from './SplitPane/SplitPane'