Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63e16d2685 |
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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); }
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user