Rename Java packages from com.cameleer3 to com.cameleer, module directories from cameleer3-* to cameleer-*, and all references throughout workflows, Dockerfiles, docs, migrations, and pom.xml. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2337 lines
88 KiB
HTML
2337 lines
88 KiB
HTML
<!--
|
|
============================================================================
|
|
CAMELEER v3 — Route Detail Page (Light Theme)
|
|
============================================================================
|
|
|
|
DESIGN NOTES
|
|
============
|
|
|
|
PAGE PURPOSE:
|
|
This is the Route Detail page — what a Camel developer sees after clicking
|
|
on a specific route (e.g., "order-processing") from the dashboard. It shows
|
|
everything about that route: flow diagram, per-processor stats, performance
|
|
metrics, recent executions, and error patterns.
|
|
|
|
LAYOUT:
|
|
- Sidebar (220px, warm charcoal) — same app/route tree as dashboard,
|
|
with this route highlighted under its parent application.
|
|
- Topbar — breadcrumb showing Apps > order-service > order-processing,
|
|
same search, env badge, user avatar.
|
|
- Main content scrolls vertically:
|
|
1. Route Header Card — status, uptime, version, inflight exchanges
|
|
2. Route Diagram + Processor Stats — side-by-side, the diagram shows
|
|
the DAG of processors with execution overlay, the table shows
|
|
per-processor performance data.
|
|
3. Tabbed section — Performance | Recent Executions | Error Patterns
|
|
|
|
DESIGN SYSTEM:
|
|
Identical to mock-v2-light.html:
|
|
- Color palette: warm parchment #F5F2ED, amber-gold #C6820E, charcoal #2C2520
|
|
- Typography: DM Sans body + JetBrains Mono for data
|
|
- Same sidebar, topbar, card styles, shadows, border radius, status colors
|
|
|
|
DATA MODEL:
|
|
Route "order-processing" with processors:
|
|
- from(direct:order-intake)
|
|
- process(OrderValidator)
|
|
- to(sql:INSERT INTO orders...)
|
|
- choice() [when(header.priority == 'HIGH')]
|
|
- to(http:payment-api/charge)
|
|
- process(ResponseMapper)
|
|
- to(kafka:order-completed)
|
|
- Error handler: to(dead-letter:failed-orders)
|
|
|
|
OPTIMIZED FOR: 1920x1080
|
|
|
|
============================================================================
|
|
-->
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=1920">
|
|
<title>Cameleer — Route: order-processing</title>
|
|
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
|
<style>
|
|
/* ==========================================================================
|
|
RESET & FOUNDATIONS (identical to v2)
|
|
========================================================================== */
|
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
|
|
:root {
|
|
/* Surface palette (warm parchment) */
|
|
--bg-body: #F5F2ED;
|
|
--bg-surface: #FFFFFF;
|
|
--bg-raised: #FAF8F5;
|
|
--bg-inset: #F0EDE8;
|
|
--bg-hover: #F5F0EA;
|
|
|
|
/* Sidebar (warm charcoal) */
|
|
--sidebar-bg: #2C2520;
|
|
--sidebar-hover: #3A322C;
|
|
--sidebar-active: #4A3F38;
|
|
--sidebar-text: #BFB5A8;
|
|
--sidebar-muted: #7A6F63;
|
|
|
|
/* Text */
|
|
--text-primary: #1A1612;
|
|
--text-secondary: #5C5347;
|
|
--text-muted: #9C9184;
|
|
--text-faint: #C4BAB0;
|
|
|
|
/* Borders */
|
|
--border: #E4DFD8;
|
|
--border-subtle: #EDE9E3;
|
|
|
|
/* Brand accent (amber-gold) */
|
|
--amber: #C6820E;
|
|
--amber-light: #F0D9A8;
|
|
--amber-bg: #FDF6E9;
|
|
--amber-deep: #8B5A06;
|
|
|
|
/* Status colors (warm) */
|
|
--success: #3D7C47;
|
|
--success-bg: #EFF7F0;
|
|
--success-border: #C2DFC6;
|
|
--warning: #C27516;
|
|
--warning-bg: #FEF5E7;
|
|
--warning-border: #F0D9A8;
|
|
--error: #C0392B;
|
|
--error-bg: #FDF0EE;
|
|
--error-border: #F0C4BE;
|
|
--running: #1A7F8E;
|
|
--running-bg: #E8F5F7;
|
|
--running-border: #B0DDE4;
|
|
|
|
/* Typography */
|
|
--font-body: 'DM Sans', system-ui, -apple-system, sans-serif;
|
|
--font-mono: 'JetBrains Mono', 'Fira Code', monospace;
|
|
|
|
/* Spacing & Radii */
|
|
--radius-sm: 5px;
|
|
--radius-md: 8px;
|
|
--radius-lg: 12px;
|
|
|
|
/* Shadows */
|
|
--shadow-sm: 0 1px 2px rgba(44, 37, 32, 0.06);
|
|
--shadow-md: 0 2px 8px rgba(44, 37, 32, 0.08);
|
|
--shadow-lg: 0 4px 16px rgba(44, 37, 32, 0.10);
|
|
--shadow-card: 0 1px 3px rgba(44, 37, 32, 0.04), 0 0 0 1px rgba(44, 37, 32, 0.04);
|
|
}
|
|
|
|
html { font-size: 14px; }
|
|
body {
|
|
font-family: var(--font-body);
|
|
background: var(--bg-body);
|
|
color: var(--text-primary);
|
|
line-height: 1.5;
|
|
min-height: 100vh;
|
|
-webkit-font-smoothing: antialiased;
|
|
}
|
|
|
|
::-webkit-scrollbar { width: 6px; height: 6px; }
|
|
::-webkit-scrollbar-track { background: transparent; }
|
|
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
|
|
::-webkit-scrollbar-thumb:hover { background: var(--text-faint); }
|
|
|
|
/* ==========================================================================
|
|
ANIMATIONS
|
|
========================================================================== */
|
|
@keyframes fadeIn {
|
|
from { opacity: 0; transform: translateY(6px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
@keyframes pulse {
|
|
0%, 100% { box-shadow: 0 0 0 0 rgba(61, 124, 71, 0.35); }
|
|
50% { box-shadow: 0 0 0 5px rgba(61, 124, 71, 0); }
|
|
}
|
|
@keyframes flowPulse {
|
|
0% { stroke-dashoffset: 0; }
|
|
100% { stroke-dashoffset: -16; }
|
|
}
|
|
.animate-in { animation: fadeIn 0.3s ease-out both; }
|
|
.delay-1 { animation-delay: 0.04s; }
|
|
.delay-2 { animation-delay: 0.08s; }
|
|
.delay-3 { animation-delay: 0.12s; }
|
|
.delay-4 { animation-delay: 0.16s; }
|
|
.delay-5 { animation-delay: 0.20s; }
|
|
|
|
/* ==========================================================================
|
|
LAYOUT
|
|
========================================================================== */
|
|
.app {
|
|
display: flex;
|
|
height: 100vh;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* ==========================================================================
|
|
SIDEBAR (220px, warm charcoal) — identical to v2
|
|
========================================================================== */
|
|
.sidebar {
|
|
width: 220px;
|
|
flex-shrink: 0;
|
|
background: var(--sidebar-bg);
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.sidebar-logo {
|
|
padding: 16px 18px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
border-bottom: 1px solid rgba(255,255,255,0.06);
|
|
}
|
|
|
|
.sidebar-logo .brand {
|
|
font-family: var(--font-mono);
|
|
font-weight: 600;
|
|
font-size: 15px;
|
|
color: var(--amber-light);
|
|
letter-spacing: -0.3px;
|
|
}
|
|
|
|
.sidebar-logo .version {
|
|
font-family: var(--font-mono);
|
|
font-size: 10px;
|
|
color: var(--sidebar-muted);
|
|
margin-left: 2px;
|
|
}
|
|
|
|
.sidebar-search { padding: 10px 12px; }
|
|
|
|
.sidebar-search input {
|
|
width: 100%;
|
|
background: rgba(255,255,255,0.06);
|
|
border: 1px solid rgba(255,255,255,0.08);
|
|
border-radius: var(--radius-sm);
|
|
padding: 6px 10px 6px 28px;
|
|
color: var(--sidebar-text);
|
|
font-family: var(--font-body);
|
|
font-size: 12px;
|
|
outline: none;
|
|
transition: border-color 0.15s;
|
|
}
|
|
|
|
.sidebar-search input::placeholder { color: var(--sidebar-muted); }
|
|
.sidebar-search input:focus { border-color: rgba(198, 130, 14, 0.4); }
|
|
|
|
.sidebar-search-wrap { position: relative; }
|
|
.sidebar-search-wrap .search-icon {
|
|
position: absolute;
|
|
left: 9px;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
color: var(--sidebar-muted);
|
|
font-size: 12px;
|
|
}
|
|
|
|
.sidebar-section {
|
|
padding: 14px 12px 5px;
|
|
font-size: 10px;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 1.2px;
|
|
color: var(--sidebar-muted);
|
|
}
|
|
|
|
.sidebar-items {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
padding: 0 6px;
|
|
}
|
|
|
|
.sidebar-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 7px 12px;
|
|
border-radius: var(--radius-sm);
|
|
color: var(--sidebar-text);
|
|
font-size: 13px;
|
|
cursor: pointer;
|
|
transition: all 0.12s;
|
|
text-decoration: none;
|
|
border-left: 3px solid transparent;
|
|
margin-bottom: 1px;
|
|
}
|
|
|
|
.sidebar-item:hover { background: var(--sidebar-hover); color: #E8DFD4; }
|
|
.sidebar-item.active { background: var(--sidebar-active); color: var(--amber-light); border-left-color: var(--amber); }
|
|
|
|
.sidebar-item .health {
|
|
width: 7px;
|
|
height: 7px;
|
|
border-radius: 50%;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.health-live { background: #5DB866; box-shadow: 0 0 6px rgba(93, 184, 102, 0.4); }
|
|
.health-stale { background: var(--warning); }
|
|
|
|
.sidebar-item .item-info { flex: 1; min-width: 0; }
|
|
.sidebar-item .item-name { font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
.sidebar-item .item-meta { font-size: 11px; color: var(--sidebar-muted); font-family: var(--font-mono); }
|
|
|
|
.sidebar-item .item-count {
|
|
font-family: var(--font-mono);
|
|
font-size: 11px;
|
|
color: var(--sidebar-muted);
|
|
background: rgba(255,255,255,0.06);
|
|
padding: 1px 6px;
|
|
border-radius: 10px;
|
|
}
|
|
|
|
.sidebar-item.active .item-count { background: rgba(198, 130, 14, 0.2); color: var(--amber-light); }
|
|
|
|
.sidebar-divider {
|
|
height: 1px;
|
|
background: rgba(255,255,255,0.06);
|
|
margin: 6px 12px;
|
|
}
|
|
|
|
.sidebar-agents-header {
|
|
padding: 14px 12px 6px;
|
|
font-size: 10px;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 1.2px;
|
|
color: var(--sidebar-muted);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.sidebar-agent-badge {
|
|
font-family: var(--font-mono);
|
|
font-size: 10px;
|
|
padding: 1px 6px;
|
|
border-radius: 10px;
|
|
background: rgba(93, 184, 102, 0.15);
|
|
color: #5DB866;
|
|
}
|
|
|
|
.agent-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 6px 12px;
|
|
margin: 0 6px 2px;
|
|
border-radius: var(--radius-sm);
|
|
font-size: 11px;
|
|
color: var(--sidebar-text);
|
|
transition: background 0.1s;
|
|
}
|
|
|
|
.agent-item:hover { background: var(--sidebar-hover); }
|
|
|
|
.agent-dot {
|
|
width: 6px;
|
|
height: 6px;
|
|
border-radius: 50%;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.agent-info { flex: 1; min-width: 0; }
|
|
.agent-name { font-family: var(--font-mono); font-size: 11px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
.agent-detail { font-size: 10px; color: var(--sidebar-muted); }
|
|
|
|
.agent-stats-col {
|
|
text-align: right;
|
|
font-family: var(--font-mono);
|
|
font-size: 10px;
|
|
color: var(--sidebar-muted);
|
|
}
|
|
|
|
.agent-tps { color: var(--sidebar-text); }
|
|
|
|
.sidebar-bottom {
|
|
border-top: 1px solid rgba(255,255,255,0.06);
|
|
padding: 6px;
|
|
}
|
|
|
|
.sidebar-bottom .sidebar-item {
|
|
font-size: 12px;
|
|
color: var(--sidebar-muted);
|
|
border-left: 3px solid transparent;
|
|
}
|
|
|
|
.sidebar-bottom .sidebar-item:hover { color: var(--sidebar-text); }
|
|
|
|
/* ==========================================================================
|
|
MAIN CONTENT AREA
|
|
========================================================================== */
|
|
.main {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
min-width: 0;
|
|
}
|
|
|
|
/* Top bar */
|
|
.topbar {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
padding: 0 24px;
|
|
height: 48px;
|
|
flex-shrink: 0;
|
|
background: var(--bg-surface);
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
|
|
.topbar-breadcrumb {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
font-size: 13px;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.topbar-breadcrumb .crumb-link { color: var(--text-muted); cursor: pointer; transition: color 0.12s; }
|
|
.topbar-breadcrumb .crumb-link:hover { color: var(--amber); }
|
|
.topbar-breadcrumb .crumb-active { color: var(--text-primary); font-weight: 600; }
|
|
.topbar-breadcrumb .crumb-sep { color: var(--text-faint); font-size: 11px; }
|
|
|
|
.topbar-search {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 5px 12px;
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius-sm);
|
|
background: var(--bg-raised);
|
|
color: var(--text-muted);
|
|
font-size: 12px;
|
|
cursor: pointer;
|
|
transition: border-color 0.15s;
|
|
min-width: 280px;
|
|
}
|
|
|
|
.topbar-search:hover { border-color: var(--text-faint); }
|
|
|
|
.topbar-kbd {
|
|
font-family: var(--font-mono);
|
|
font-size: 10px;
|
|
background: var(--bg-surface);
|
|
border: 1px solid var(--border);
|
|
padding: 0 4px;
|
|
border-radius: 3px;
|
|
color: var(--text-faint);
|
|
margin-left: auto;
|
|
}
|
|
|
|
.topbar-right {
|
|
margin-left: auto;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
}
|
|
|
|
.topbar-env {
|
|
font-family: var(--font-mono);
|
|
font-size: 10px;
|
|
font-weight: 600;
|
|
padding: 3px 10px;
|
|
border-radius: 10px;
|
|
background: var(--success-bg);
|
|
color: var(--success);
|
|
border: 1px solid var(--success-border);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
}
|
|
|
|
.topbar-shift {
|
|
font-family: var(--font-mono);
|
|
font-size: 10px;
|
|
padding: 3px 10px;
|
|
border-radius: 10px;
|
|
background: var(--running-bg);
|
|
color: var(--running);
|
|
border: 1px solid var(--running-border);
|
|
}
|
|
|
|
.topbar-user {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
font-size: 12px;
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.topbar-avatar {
|
|
width: 28px;
|
|
height: 28px;
|
|
border-radius: 50%;
|
|
background: var(--amber-bg);
|
|
color: var(--amber);
|
|
font-weight: 600;
|
|
font-size: 11px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border: 1px solid var(--amber-light);
|
|
}
|
|
|
|
/* Content scroll container */
|
|
.content {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
padding: 20px 24px 40px;
|
|
min-width: 0;
|
|
}
|
|
|
|
/* ==========================================================================
|
|
ROUTE HEADER CARD
|
|
========================================================================== */
|
|
.route-header {
|
|
background: var(--bg-surface);
|
|
border: 1px solid var(--border-subtle);
|
|
border-radius: var(--radius-lg);
|
|
box-shadow: var(--shadow-card);
|
|
padding: 16px 20px;
|
|
margin-bottom: 16px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 20px;
|
|
}
|
|
|
|
.route-header-main { flex: 1; min-width: 0; }
|
|
|
|
.route-title-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.route-title {
|
|
font-size: 18px;
|
|
font-weight: 700;
|
|
color: var(--text-primary);
|
|
letter-spacing: -0.3px;
|
|
}
|
|
|
|
.route-status-badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 5px;
|
|
padding: 3px 12px;
|
|
border-radius: 12px;
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
letter-spacing: 0.3px;
|
|
}
|
|
|
|
.route-status-started {
|
|
background: var(--success-bg);
|
|
color: var(--success);
|
|
border: 1px solid var(--success-border);
|
|
}
|
|
|
|
.route-status-dot {
|
|
width: 6px;
|
|
height: 6px;
|
|
border-radius: 50%;
|
|
background: var(--success);
|
|
animation: pulse 2s ease-in-out infinite;
|
|
}
|
|
|
|
.route-subtitle {
|
|
font-size: 12px;
|
|
color: var(--text-muted);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
}
|
|
|
|
.route-subtitle .mono {
|
|
font-family: var(--font-mono);
|
|
font-size: 11px;
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.route-subtitle .dot-sep {
|
|
width: 3px;
|
|
height: 3px;
|
|
border-radius: 50%;
|
|
background: var(--text-faint);
|
|
}
|
|
|
|
.route-header-stats {
|
|
display: flex;
|
|
gap: 16px;
|
|
}
|
|
|
|
.route-stat {
|
|
text-align: center;
|
|
padding: 8px 16px;
|
|
border-radius: var(--radius-md);
|
|
background: var(--bg-raised);
|
|
border: 1px solid var(--border-subtle);
|
|
min-width: 100px;
|
|
}
|
|
|
|
.route-stat-label {
|
|
font-size: 10px;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
color: var(--text-muted);
|
|
margin-bottom: 2px;
|
|
}
|
|
|
|
.route-stat-value {
|
|
font-family: var(--font-mono);
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.route-stat-value.val-green { color: var(--success); }
|
|
.route-stat-value.val-amber { color: var(--amber); }
|
|
.route-stat-value.val-teal { color: var(--running); }
|
|
.route-stat-value.val-error { color: var(--error); }
|
|
|
|
.route-stat-detail {
|
|
font-size: 10px;
|
|
color: var(--text-muted);
|
|
font-family: var(--font-mono);
|
|
}
|
|
|
|
.route-header-actions {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 6px;
|
|
}
|
|
|
|
.route-action-btn {
|
|
padding: 6px 14px;
|
|
border-radius: var(--radius-sm);
|
|
font-size: 11px;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
transition: all 0.15s;
|
|
font-family: var(--font-body);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.route-action-btn.primary {
|
|
background: var(--amber);
|
|
color: white;
|
|
border: none;
|
|
}
|
|
|
|
.route-action-btn.primary:hover { background: var(--amber-deep); }
|
|
|
|
.route-action-btn.secondary {
|
|
background: none;
|
|
border: 1px solid var(--border);
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.route-action-btn.secondary:hover { border-color: var(--text-faint); color: var(--text-primary); }
|
|
|
|
/* ==========================================================================
|
|
ROUTE DIAGRAM + PROCESSOR STATS (side by side)
|
|
========================================================================== */
|
|
.diagram-stats-row {
|
|
display: grid;
|
|
grid-template-columns: 380px 1fr;
|
|
gap: 16px;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
/* Route Diagram */
|
|
.diagram-card {
|
|
background: var(--bg-surface);
|
|
border: 1px solid var(--border-subtle);
|
|
border-radius: var(--radius-lg);
|
|
box-shadow: var(--shadow-card);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.diagram-card-header {
|
|
padding: 10px 16px;
|
|
border-bottom: 1px solid var(--border-subtle);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.diagram-card-title {
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.diagram-legend {
|
|
display: flex;
|
|
gap: 12px;
|
|
font-size: 10px;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.legend-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
}
|
|
|
|
.legend-dot {
|
|
width: 8px;
|
|
height: 8px;
|
|
border-radius: 2px;
|
|
}
|
|
|
|
.diagram-body {
|
|
padding: 20px 16px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 0;
|
|
}
|
|
|
|
/* Processor nodes in diagram */
|
|
.diagram-node {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
width: 320px;
|
|
padding: 8px 12px;
|
|
border-radius: var(--radius-md);
|
|
border: 1px solid var(--border-subtle);
|
|
background: var(--bg-surface);
|
|
cursor: pointer;
|
|
transition: all 0.12s;
|
|
position: relative;
|
|
}
|
|
|
|
.diagram-node:hover {
|
|
box-shadow: var(--shadow-md);
|
|
border-color: var(--text-faint);
|
|
}
|
|
|
|
.diagram-node.node-healthy { border-left: 3px solid var(--success); }
|
|
.diagram-node.node-slow { border-left: 3px solid var(--warning); }
|
|
.diagram-node.node-error { border-left: 3px solid var(--error); }
|
|
.diagram-node.node-bottleneck {
|
|
border-left: 3px solid var(--error);
|
|
background: var(--warning-bg);
|
|
border-color: var(--warning-border);
|
|
}
|
|
|
|
.node-icon {
|
|
width: 28px;
|
|
height: 28px;
|
|
border-radius: var(--radius-sm);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 12px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.node-icon.icon-from { background: var(--running-bg); color: var(--running); }
|
|
.node-icon.icon-process { background: var(--amber-bg); color: var(--amber); }
|
|
.node-icon.icon-to { background: var(--success-bg); color: var(--success); }
|
|
.node-icon.icon-choice { background: #F3EEFA; color: #7C3AED; }
|
|
.node-icon.icon-error-handler { background: var(--error-bg); color: var(--error); }
|
|
|
|
.node-info { flex: 1; min-width: 0; }
|
|
|
|
.node-type {
|
|
font-size: 10px;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.node-label {
|
|
font-family: var(--font-mono);
|
|
font-size: 11px;
|
|
color: var(--text-primary);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.node-stats {
|
|
text-align: right;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.node-duration {
|
|
font-family: var(--font-mono);
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.node-duration.dur-fast { color: var(--success); }
|
|
.node-duration.dur-normal { color: var(--text-primary); }
|
|
.node-duration.dur-slow { color: var(--warning); }
|
|
.node-duration.dur-breach { color: var(--error); }
|
|
|
|
.node-counts {
|
|
font-family: var(--font-mono);
|
|
font-size: 9px;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.node-counts .err-count { color: var(--error); font-weight: 600; }
|
|
|
|
/* Bottleneck badge */
|
|
.bottleneck-badge {
|
|
position: absolute;
|
|
top: -8px;
|
|
right: 10px;
|
|
font-family: var(--font-mono);
|
|
font-size: 9px;
|
|
font-weight: 600;
|
|
padding: 1px 8px;
|
|
border-radius: 10px;
|
|
background: var(--error);
|
|
color: white;
|
|
letter-spacing: 0.3px;
|
|
}
|
|
|
|
/* Connector arrows between nodes */
|
|
.diagram-connector {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
height: 24px;
|
|
position: relative;
|
|
}
|
|
|
|
.connector-line {
|
|
width: 2px;
|
|
flex: 1;
|
|
background: var(--border);
|
|
}
|
|
|
|
.connector-arrow {
|
|
width: 0;
|
|
height: 0;
|
|
border-left: 5px solid transparent;
|
|
border-right: 5px solid transparent;
|
|
border-top: 6px solid var(--border);
|
|
}
|
|
|
|
/* Choice branch visual */
|
|
.diagram-branch {
|
|
display: flex;
|
|
gap: 12px;
|
|
align-items: flex-start;
|
|
width: 340px;
|
|
position: relative;
|
|
}
|
|
|
|
.branch-label {
|
|
font-family: var(--font-mono);
|
|
font-size: 9px;
|
|
color: var(--text-muted);
|
|
padding: 2px 8px;
|
|
background: var(--bg-raised);
|
|
border: 1px solid var(--border-subtle);
|
|
border-radius: 10px;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* Error handler section in diagram */
|
|
.diagram-error-section {
|
|
margin-top: 8px;
|
|
padding-top: 12px;
|
|
border-top: 1px dashed var(--error-border);
|
|
width: 340px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
}
|
|
|
|
.error-handler-label {
|
|
font-size: 9px;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.8px;
|
|
color: var(--error);
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
/* ==========================================================================
|
|
PROCESSOR STATS TABLE
|
|
========================================================================== */
|
|
.stats-card {
|
|
background: var(--bg-surface);
|
|
border: 1px solid var(--border-subtle);
|
|
border-radius: var(--radius-lg);
|
|
box-shadow: var(--shadow-card);
|
|
overflow: hidden;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.stats-card-header {
|
|
padding: 10px 16px;
|
|
border-bottom: 1px solid var(--border-subtle);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.stats-card-title {
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.stats-card-meta {
|
|
font-family: var(--font-mono);
|
|
font-size: 11px;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.stats-table-wrap {
|
|
overflow-x: auto;
|
|
flex: 1;
|
|
}
|
|
|
|
table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
}
|
|
|
|
thead {
|
|
background: var(--bg-raised);
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
|
|
th {
|
|
padding: 9px 14px;
|
|
text-align: left;
|
|
font-size: 10px;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.8px;
|
|
color: var(--text-muted);
|
|
white-space: nowrap;
|
|
user-select: none;
|
|
cursor: pointer;
|
|
transition: color 0.12s;
|
|
}
|
|
|
|
th:hover { color: var(--text-secondary); }
|
|
th.sorted { color: var(--amber); }
|
|
.sort-arrow { font-size: 8px; margin-left: 3px; opacity: 0.4; }
|
|
th.sorted .sort-arrow { opacity: 1; }
|
|
th.text-right { text-align: right; }
|
|
|
|
tbody tr {
|
|
border-bottom: 1px solid var(--border-subtle);
|
|
transition: background 0.08s;
|
|
cursor: pointer;
|
|
}
|
|
|
|
tbody tr:last-child { border-bottom: none; }
|
|
tbody tr:hover { background: var(--bg-hover); }
|
|
tbody tr.row-highlight { background: var(--warning-bg); }
|
|
|
|
td {
|
|
padding: 9px 14px;
|
|
font-size: 13px;
|
|
vertical-align: middle;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
td.text-right { text-align: right; }
|
|
|
|
.proc-type-badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
padding: 2px 8px;
|
|
border-radius: 10px;
|
|
font-size: 10px;
|
|
font-weight: 600;
|
|
font-family: var(--font-mono);
|
|
letter-spacing: 0.3px;
|
|
}
|
|
|
|
.proc-type-from { background: var(--running-bg); color: var(--running); }
|
|
.proc-type-process { background: var(--amber-bg); color: var(--amber-deep); }
|
|
.proc-type-to { background: var(--success-bg); color: var(--success); }
|
|
.proc-type-choice { background: #F3EEFA; color: #7C3AED; }
|
|
.proc-type-error { background: var(--error-bg); color: var(--error); }
|
|
|
|
.proc-name-cell {
|
|
font-family: var(--font-mono);
|
|
font-size: 12px;
|
|
color: var(--text-primary);
|
|
font-weight: 500;
|
|
}
|
|
|
|
.duration-cell {
|
|
font-family: var(--font-mono);
|
|
font-size: 12px;
|
|
}
|
|
|
|
.dur-fast { color: var(--success); }
|
|
.dur-normal { color: var(--text-primary); }
|
|
.dur-slow { color: var(--warning); }
|
|
.dur-breach { color: var(--error); font-weight: 600; }
|
|
|
|
.invocations-cell {
|
|
font-family: var(--font-mono);
|
|
font-size: 12px;
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.error-pct {
|
|
font-family: var(--font-mono);
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.error-pct.low { color: var(--success); }
|
|
.error-pct.medium { color: var(--warning); }
|
|
.error-pct.high { color: var(--error); }
|
|
|
|
/* Duration bar (inline sparkline) */
|
|
.dur-bar-wrap {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
}
|
|
|
|
.dur-bar {
|
|
width: 60px;
|
|
height: 6px;
|
|
background: var(--bg-inset);
|
|
border-radius: 3px;
|
|
overflow: hidden;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.dur-bar-fill {
|
|
height: 100%;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.dur-bar-fill.fill-fast { background: var(--success); }
|
|
.dur-bar-fill.fill-normal { background: var(--amber-light); }
|
|
.dur-bar-fill.fill-slow { background: var(--warning); }
|
|
.dur-bar-fill.fill-breach { background: var(--error); }
|
|
|
|
/* ==========================================================================
|
|
TABBED SECTION
|
|
========================================================================== */
|
|
.tabs-section {
|
|
background: var(--bg-surface);
|
|
border: 1px solid var(--border-subtle);
|
|
border-radius: var(--radius-lg);
|
|
box-shadow: var(--shadow-card);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.tabs-header {
|
|
display: flex;
|
|
border-bottom: 1px solid var(--border-subtle);
|
|
}
|
|
|
|
.tab-btn {
|
|
padding: 11px 20px;
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
color: var(--text-muted);
|
|
cursor: pointer;
|
|
border: none;
|
|
border-bottom: 2px solid transparent;
|
|
background: none;
|
|
font-family: var(--font-body);
|
|
transition: all 0.15s;
|
|
}
|
|
|
|
.tab-btn:hover { color: var(--text-secondary); }
|
|
.tab-btn.active { color: var(--amber); border-bottom-color: var(--amber); }
|
|
|
|
.tab-btn .tab-count {
|
|
font-family: var(--font-mono);
|
|
font-size: 10px;
|
|
padding: 1px 6px;
|
|
border-radius: 10px;
|
|
background: var(--bg-inset);
|
|
color: var(--text-muted);
|
|
margin-left: 6px;
|
|
}
|
|
|
|
.tab-btn.active .tab-count {
|
|
background: var(--amber-bg);
|
|
color: var(--amber-deep);
|
|
}
|
|
|
|
.tab-content { display: none; }
|
|
.tab-content.active { display: block; }
|
|
|
|
/* Performance tab */
|
|
.perf-summary {
|
|
display: grid;
|
|
grid-template-columns: repeat(4, 1fr);
|
|
gap: 12px;
|
|
padding: 16px;
|
|
border-bottom: 1px solid var(--border-subtle);
|
|
}
|
|
|
|
.perf-stat {
|
|
background: var(--bg-raised);
|
|
border: 1px solid var(--border-subtle);
|
|
border-radius: var(--radius-md);
|
|
padding: 12px 14px;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.perf-stat::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 2px;
|
|
}
|
|
|
|
.perf-stat.stat-throughput::before { background: linear-gradient(90deg, var(--running), transparent); }
|
|
.perf-stat.stat-error-rate::before { background: linear-gradient(90deg, var(--error), transparent); }
|
|
.perf-stat.stat-avg-latency::before { background: linear-gradient(90deg, var(--amber), transparent); }
|
|
.perf-stat.stat-p99::before { background: linear-gradient(90deg, var(--warning), transparent); }
|
|
|
|
.perf-stat-label {
|
|
font-size: 10px;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
color: var(--text-muted);
|
|
margin-bottom: 2px;
|
|
}
|
|
|
|
.perf-stat-value {
|
|
font-family: var(--font-mono);
|
|
font-size: 20px;
|
|
font-weight: 600;
|
|
line-height: 1.2;
|
|
}
|
|
|
|
.perf-stat-value.val-teal { color: var(--running); }
|
|
.perf-stat-value.val-error { color: var(--error); }
|
|
.perf-stat-value.val-amber { color: var(--amber); }
|
|
.perf-stat-value.val-warn { color: var(--warning); }
|
|
|
|
.perf-stat-unit {
|
|
font-size: 12px;
|
|
color: var(--text-muted);
|
|
margin-left: 2px;
|
|
}
|
|
|
|
.perf-stat-trend {
|
|
font-family: var(--font-mono);
|
|
font-size: 11px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 2px;
|
|
margin-left: 6px;
|
|
}
|
|
|
|
.trend-up-good { color: var(--success); }
|
|
.trend-up-bad { color: var(--error); }
|
|
.trend-down-good { color: var(--success); }
|
|
.trend-down-bad { color: var(--error); }
|
|
.trend-flat { color: var(--text-muted); }
|
|
|
|
.perf-stat-detail {
|
|
font-size: 10px;
|
|
color: var(--text-muted);
|
|
margin-top: 2px;
|
|
}
|
|
|
|
/* Charts row */
|
|
.charts-row {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr 1fr;
|
|
gap: 0;
|
|
}
|
|
|
|
.chart-panel {
|
|
padding: 16px;
|
|
border-right: 1px solid var(--border-subtle);
|
|
}
|
|
|
|
.chart-panel:last-child { border-right: none; }
|
|
|
|
.chart-panel-title {
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
color: var(--text-muted);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
margin-bottom: 10px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.chart-panel-value {
|
|
font-family: var(--font-mono);
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.chart-area-wrapper {
|
|
height: 100px;
|
|
position: relative;
|
|
}
|
|
|
|
.chart-line { fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
|
|
.chart-area { opacity: 0.12; }
|
|
.chart-grid-line { stroke: var(--border-subtle); stroke-width: 1; stroke-dasharray: 3 3; }
|
|
.chart-label { font-family: var(--font-mono); font-size: 9px; fill: var(--text-faint); }
|
|
.sla-line { stroke: var(--error); stroke-width: 1; stroke-dasharray: 4 3; opacity: 0.5; }
|
|
.sla-label-text { fill: var(--error); font-size: 8px; font-family: var(--font-mono); opacity: 0.6; }
|
|
|
|
/* Executions tab */
|
|
.exec-table-wrap {
|
|
overflow-x: auto;
|
|
}
|
|
|
|
.exec-table-header {
|
|
padding: 10px 16px;
|
|
border-bottom: 1px solid var(--border-subtle);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.exec-table-meta {
|
|
font-family: var(--font-mono);
|
|
font-size: 11px;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.live-btn {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 5px 12px;
|
|
border: 1px solid var(--success-border);
|
|
border-radius: var(--radius-sm);
|
|
background: var(--success-bg);
|
|
color: var(--success);
|
|
font-family: var(--font-mono);
|
|
font-size: 11px;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
transition: all 0.15s;
|
|
}
|
|
|
|
.live-btn:hover { background: #E2F0E4; }
|
|
|
|
.live-dot {
|
|
width: 6px;
|
|
height: 6px;
|
|
border-radius: 50%;
|
|
background: var(--success);
|
|
animation: pulse 2s ease-in-out infinite;
|
|
}
|
|
|
|
.status-badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 5px;
|
|
padding: 2px 10px;
|
|
border-radius: 12px;
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
letter-spacing: 0.2px;
|
|
}
|
|
|
|
.status-success { background: var(--success-bg); color: var(--success); border: 1px solid var(--success-border); }
|
|
.status-warning { background: var(--warning-bg); color: var(--warning); border: 1px solid var(--warning-border); }
|
|
.status-error { background: var(--error-bg); color: var(--error); border: 1px solid var(--error-border); }
|
|
.status-running { background: var(--running-bg); color: var(--running); border: 1px solid var(--running-border); }
|
|
|
|
.status-dot {
|
|
width: 5px;
|
|
height: 5px;
|
|
border-radius: 50%;
|
|
background: currentColor;
|
|
}
|
|
|
|
.time-relative { color: var(--text-secondary); font-size: 13px; }
|
|
.time-absolute { font-family: var(--font-mono); font-size: 10px; color: var(--text-muted); display: block; }
|
|
|
|
.biz-id { font-family: var(--font-mono); font-weight: 500; color: var(--running); font-size: 12px; }
|
|
|
|
tbody tr.row-success { border-left: 3px solid transparent; }
|
|
tbody tr.row-error { border-left: 3px solid var(--error); }
|
|
tbody tr.row-warning { border-left: 3px solid var(--warning); }
|
|
tbody tr.row-running { border-left: 3px solid var(--running); }
|
|
|
|
/* Error patterns tab */
|
|
.error-patterns {
|
|
padding: 16px;
|
|
}
|
|
|
|
.error-pattern {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 14px;
|
|
padding: 14px 16px;
|
|
background: var(--bg-raised);
|
|
border: 1px solid var(--border-subtle);
|
|
border-left: 3px solid var(--error);
|
|
border-radius: var(--radius-md);
|
|
margin-bottom: 10px;
|
|
transition: box-shadow 0.12s;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.error-pattern:hover { box-shadow: var(--shadow-md); }
|
|
|
|
.error-pattern-count {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
min-width: 50px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.error-count-num {
|
|
font-family: var(--font-mono);
|
|
font-size: 22px;
|
|
font-weight: 700;
|
|
color: var(--error);
|
|
line-height: 1;
|
|
}
|
|
|
|
.error-count-label {
|
|
font-size: 9px;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.error-pattern-info { flex: 1; min-width: 0; }
|
|
|
|
.error-exception-class {
|
|
font-family: var(--font-mono);
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
color: var(--error);
|
|
margin-bottom: 3px;
|
|
}
|
|
|
|
.error-message-preview {
|
|
font-family: var(--font-mono);
|
|
font-size: 11px;
|
|
color: var(--text-secondary);
|
|
line-height: 1.5;
|
|
margin-bottom: 6px;
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 2;
|
|
-webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.error-pattern-meta {
|
|
display: flex;
|
|
gap: 16px;
|
|
font-size: 10px;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.error-pattern-meta .mono {
|
|
font-family: var(--font-mono);
|
|
}
|
|
|
|
.error-trend-bar {
|
|
display: flex;
|
|
align-items: flex-end;
|
|
gap: 2px;
|
|
height: 24px;
|
|
flex-shrink: 0;
|
|
align-self: center;
|
|
}
|
|
|
|
.error-trend-bar-segment {
|
|
width: 4px;
|
|
background: var(--error-border);
|
|
border-radius: 1px;
|
|
transition: height 0.2s;
|
|
}
|
|
|
|
.error-trend-bar-segment.active { background: var(--error); }
|
|
|
|
/* ==========================================================================
|
|
KEYBOARD SHORTCUTS BAR
|
|
========================================================================== */
|
|
.shortcuts-bar {
|
|
position: fixed;
|
|
bottom: 12px;
|
|
right: 12px;
|
|
display: flex;
|
|
gap: 10px;
|
|
z-index: 50;
|
|
animation: fadeIn 0.5s ease-out 0.5s both;
|
|
}
|
|
|
|
.shortcut-hint {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 5px;
|
|
font-size: 10px;
|
|
color: var(--text-muted);
|
|
background: var(--bg-surface);
|
|
border: 1px solid var(--border-subtle);
|
|
border-radius: var(--radius-sm);
|
|
padding: 4px 8px;
|
|
box-shadow: var(--shadow-sm);
|
|
}
|
|
|
|
.shortcut-hint kbd {
|
|
font-family: var(--font-mono);
|
|
font-size: 9px;
|
|
background: var(--bg-raised);
|
|
border: 1px solid var(--border);
|
|
border-radius: 3px;
|
|
padding: 0 4px;
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.mono { font-family: var(--font-mono); }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="app">
|
|
|
|
<!-- ====================================================================
|
|
SIDEBAR
|
|
==================================================================== -->
|
|
<aside class="sidebar">
|
|
<div class="sidebar-logo">
|
|
<svg width="24" height="26" viewBox="940 300 900 1000" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<path style="fill:#C6820E" d="M 1385.2768,1205.0273 c -1.7727,-1.0809 -3.4419,-2.9917 -3.7093,-4.2463 -0.2674,-1.2545 -0.6851,-23.881 -0.9282,-50.281 -0.4544,-49.3389 -1.4003,-65.7355 -5.6645,-98.1921 -10.4552,-79.57719 -28.3441,-125.96796 -62.5438,-162.19311 -9.2529,-9.80096 -19.9748,-18.38696 -33.931,-27.17162 -12.6119,-7.93853 -24.8581,-13.73088 -49.5,-23.41316 -41.7081,-16.38789 -67.8677,-32.29827 -89.0588,-54.16599 -34.7586,-35.86827 -54.3653,-83.30392 -59.0504,-142.86402 -0.8357,-10.6245 -0.92,-22.5842 -0.2891,-41 0.4899,-14.3 0.8164,-35.225 0.7255,-46.5 -0.1983,-24.59199 -2.0251,-34.76973 -8.4617,-47.14159 -6.2021,-11.92135 -10.364,-15.10789 -36.2345,-27.74305 -25.4032,-12.40694 -27.7701,-13.30168 -35.4717,-13.40961 -4.63013,-0.0649 -7.47783,0.72754 -15.6593,4.35747 -9.86218,4.37563 -10.15161,4.43672 -21,4.43267 -10.86566,-0.004 -11.08917,-0.0517 -18.30169,-3.90103 -17.43524,-9.3052 -21.50075,-23.5772 -13.55784,-47.59486 2.73631,-8.27402 10.00732,-22.43593 14.69263,-28.61719 5.4984,-7.25394 10.89014,-11.83885 20.83968,-17.72117 15.06768,-8.90827 29.60254,-11.98884 63.90372,-13.54403 l 16.5765,-0.75156 14.9235,-7.05735 c 8.2079,-3.88154 17.1735,-7.76831 19.9235,-8.63727 7.7052,-2.43474 21.059,-4.67186 27.8605,-4.66741 8.0518,0.005 22.643,2.41202 30.0139,4.95066 l 5.8744,2.02321 4.8856,-4.09041 c 10.341,-8.65797 18.6496,-12.95738 28.96,-14.98583 6.9966,-1.37649 26.3532,-0.64631 32.1116,1.21134 4.5531,1.46885 5.4951,3.7902 6.3689,15.69592 1.5167,20.66426 -5.0112,40.44987 -18.1772,55.09363 -4.3065,4.78983 -4.5016,5.25488 -3.4977,8.33516 4.5184,13.86447 5.3154,38.07517 2.1537,65.42564 -5.169,44.71349 -5.0411,70.7797 0.5404,110.15637 6.8135,48.06863 22.3335,73.51874 48.2051,79.04779 24.1748,5.16643 45.2921,-5.78181 66.8353,-34.65077 4.809,-6.4442 11.1363,-14.93006 14.0608,-18.85747 7.3865,-9.91943 25.6102,-27.11708 35.952,-33.92766 12.5235,-8.24739 26.8808,-14.74833 42.8527,-19.40354 15.4108,-4.49168 26.7091,-9.74984 36.2432,-16.86731 3.4338,-2.56341 13.3338,-12.03104 22,-21.03916 26.2356,-27.27067 44.5755,-40.32368 66.9928,-47.68062 19.1052,-6.26998 35.4927,-7.73718 50.8681,-4.55427 37.0856,7.67726 63.5507,26.77589 100.2888,72.37362 16.6011,20.60463 29.9711,32.07977 51.6071,44.29313 39.3131,22.19195 50.1228,30.50985 68.8076,52.94655 5.4963,6.6 12.9187,14.91604 16.4941,18.4801 33.6796,33.57259 54.1965,72.51092 61.6587,117.0199 2.7315,16.29242 3.3374,26.62538 3.1861,54.33713 l -0.1465,26.83714 -2.7975,2.40572 c -3.9132,3.36522 -7.2806,3.99163 -11.2591,2.09442 -6.4731,-3.08682 -6.5715,-3.57833 -6.878,-34.36929 -0.2908,-29.2175 -2.0265,-46.13705 -6.6479,-64.80512 -3.2997,-13.32891 -12.2529,-34.61943 -17.0313,-40.5 l -2.0314,-2.5 0.5698,3.5 c 5.8574,35.97875 4.2855,72.40287 -5.3946,125 -8.4016,45.65054 -4.3665,69.39588 20.7318,122 11.4175,23.93009 12.5452,27.25907 12.4212,36.6684 -0.1018,7.7227 -2.5346,19.5162 -13.1674,63.8316 -1.0558,4.4 -3.5336,14.975 -5.5063,23.5 -1.9727,8.525 -4.2259,18.2 -5.0072,21.5 -0.7812,3.3 -3.4495,15.225 -5.9296,26.5 -9.1636,41.6596 -13.4372,59.8787 -14.5204,61.9027 -2.4994,4.6702 -5.2312,5.0973 -32.6024,5.0973 H 1765.8 l -3.4,-3.4 c -2.3518,-2.3518 -3.4,-4.3226 -3.4,-6.3925 0,-1.6458 2.2347,-12.1533 4.966,-23.35 8.6906,-35.6259 11.6969,-54.778 12.6921,-80.8575 1.3475,-35.3073 -4.6406,-62.7687 -18.7825,-86.137 -7.6672,-12.66954 -11.9163,-17.84148 -29.8756,-36.36415 -22.9362,-23.65574 -34.6222,-39.72583 -47.9268,-65.90697 -5.7294,-11.27448 -13.6061,-31.99995 -15.5051,-40.79778 -0.3212,-1.48824 -1.0176,-3.84619 -1.5475,-5.2399 l -0.9634,-2.534 -11.7786,7.15145 c -38.634,23.45687 -74.5513,34.71091 -124.1895,38.91257 -14.5402,1.23075 -58.2359,0.66344 -71.9142,-0.93369 -5.4419,-0.63542 -6.4542,-0.49723 -7.25,0.98972 -0.5545,1.03621 -0.9249,60.63835 -0.9249,148.84365 0,161.6262 0.4025,151.2052 -5.9673,154.4992 -2.3865,1.2341 -7.4633,1.5162 -27.233,1.5132 -22.3926,-0 -24.5527,-0.158 -27.5229,-1.969 z M 1424,1045.6452 c 0,-87.21053 0.3878,-144.17994 1.0365,-152.24998 1.8115,-22.53829 7.2373,-44.8067 16.1142,-66.13567 20.7842,-49.93942 66.8961,-95.01414 129.8493,-126.92865 14.9469,-7.57742 29.2834,-13.56192 41.5,-17.32336 3.85,-1.1854 7.9,-2.55654 9,-3.04699 6.0644,-2.70386 29.8837,-6.94397 39.0685,-6.95465 16.5354,-0.0192 29.4209,10.53866 32.4491,26.58755 1.5763,8.35375 1.0812,11.63345 -4.5147,29.90655 -10.1916,33.28037 -13.3229,61.96134 -10.0442,92 3.2262,29.55737 10.5673,53.87028 23.6898,78.45848 11.023,20.65405 21.7624,35.21113 38.7623,52.54152 17.2909,17.62693 24.0427,25.40931 30.3653,35 26.0619,39.5327 33.1081,82.6442 23.6173,144.5 -1.6936,11.0379 -7.1852,37.5413 -10.412,50.25 l -0.6982,2.75 h 11.041 c 9.6675,0 11.1002,-0.2177 11.5165,-1.75 0.2615,-0.9625 1.1691,-4.9 2.0169,-8.75 4.2214,-19.1689 6.8202,-30.513 8.1326,-35.5 1.361,-5.1717 9.3524,-39.8413 18.5555,-80.5 2.2408,-9.9 5.3554,-23.4 6.9213,-30 4.9646,-20.9246 5.3189,-23.3077 4.1531,-27.9371 -0.5856,-2.32595 -4.0498,-10.49161 -7.6981,-18.14594 -9.222,-19.34804 -12.2871,-26.05688 -14.0769,-30.8114 -5.3001,-14.07878 -9.5733,-27.65251 -11.9961,-38.10556 -2.5547,-11.02237 -2.7797,-13.62939 -2.7612,-32 0.019,-19.36303 0.1714,-20.85991 4.7729,-47 6.161,-34.99931 7.6003,-48.16042 7.6003,-69.5 -10e-5,-33.76835 -6.5703,-63.86472 -19.8286,-90.82854 -6.3511,-12.91659 -11.7543,-20.74668 -21.037,-30.48614 -12.8375,-13.46928 -22.9472,-20.76621 -47.0954,-33.9923 -11.7394,-6.42975 -28.8576,-17.82564 -36.5,-24.2987 -10.6672,-9.03504 -17.2351,-15.91058 -32.8553,-34.39432 -16.848,-19.93653 -31.9446,-35.04489 -42.6447,-42.67772 -10.3431,-7.37816 -15.6552,-10.10769 -28,-14.38707 -9.1747,-3.18048 -10.0479,-3.29466 -25.5,-3.33475 -15.5657,-0.0404 -16.2561,0.0478 -25.4354,3.24965 -23.091,8.0544 -33.3943,15.26018 -59.5646,41.65777 -30.5973,30.86299 -38.6661,35.8414 -77,47.50846 -16.2269,4.93872 -34.1287,15.69697 -44.617,26.81301 -10.992,11.6499 -13.307,14.35965 -22.3589,26.17065 -19.0735,24.88726 -27.0799,33.14259 -41.0241,42.29946 -10.0586,6.60527 -20.3327,9.75956 -33.9507,10.42334 -37.2243,1.81441 -62.5379,-17.55119 -76.1054,-58.2228 -11.652,-34.92922 -16.8948,-93.36051 -12.158,-135.5 5.892,-52.41494 5.8488,-63.62373 -0.3099,-80.57433 -4.7448,-13.05916 -4.7675,-12.96705 5.716,-23.17933 5.7642,-5.61509 9.7601,-10.45668 11.1613,-13.52373 2.7419,-6.00142 5.1126,-15.9207 5.1319,-21.47261 L 1198,352 l -5.9651,0 c -11.169,0 -21.7558,5.31928 -28.8986,14.51992 -5.5094,7.09667 -9.8889,9.41118 -14.4261,7.62413 -22.7413,-8.95689 -39.8334,-10.59845 -57.5511,-5.52733 -3.1125,0.89085 -11.6238,4.5155 -18.9141,8.05478 -15.4059,7.47927 -22.5958,9.28006 -37.245,9.32847 -31.9108,0.10543 -56.25892,6.94953 -67.17127,18.88141 -8.24539,9.01574 -18.57001,32.98439 -17.63456,40.9387 0.20038,1.70385 1.68501,3.56683 4.30583,5.40313 6.15036,4.30931 11.64453,4.02586 24.53725,-1.26587 9.37877,-3.84945 11.49879,-4.33904 20.46275,-4.7256 12.5127,-0.5396 16.1351,0.57224 41.5,12.73763 28.326,13.58558 38.6034,16.26339 54.7762,14.27212 10.7814,-1.32746 19.6601,-4.77604 26.9813,-10.47981 10.8957,-8.48862 12.7264,-9.11418 18.2752,-6.24475 3.4802,1.79966 5.9673,6.17527 5.9673,10.49833 0,5.2273 -12.3619,16.07427 -24.373,21.38616 -8.3575,3.69608 -19.5481,6.59858 -25.4408,6.59858 -2.8524,0 -5.1862,0.40847 -5.1862,0.90771 0,0.49925 1.1261,3.08675 2.5024,5.75 7.4849,14.48347 10.3469,40.93277 8.5814,79.30477 -1.8809,40.87852 -1.063,65.17107 3.0314,90.03752 1.9205,11.66347 7.4026,31.07934 11.5058,40.75 3.6541,8.61199 4.8919,11.32366 8.5592,18.75 7.5496,15.28823 16.3315,27.16362 30.7439,41.57393 20.487,20.48403 39.3629,32.17675 72.4974,44.90876 32.2544,12.39381 46.3332,19.15119 64.0627,30.7482 24.2965,15.89251 40.6199,31.86253 55.9811,54.76911 15.3367,22.87022 26.7091,49.93352 35.5759,84.66142 7.4833,29.30908 14.5942,79.14828 16.5733,116.15948 0.3771,7.0514 0.7727,14.0264 0.879,15.5 0.1064,1.4735 0.2639,17.1916 0.35,34.9291 L 1403,1185 h 10.5 10.5 z m 109.6148,-166.12974 c 32.3584,-3.61532 59.7448,-11.3892 85.9116,-24.38672 13.8501,-6.87965 33.6049,-18.83328 37.1094,-22.455 0.7986,-0.82533 0.878,-3.10315 0.2676,-7.67374 -0.4775,-3.575 -0.9114,-15.05 -0.9643,-25.5 -0.1555,-30.74924 3.0015,-49.74257 13.6751,-82.27105 3.3477,-10.20267 3.1984,-15.57022 -0.5373,-19.30587 -2.563,-2.563 -3.6408,-2.92269 -8.75,-2.91989 -17.9704,0.01 -50.057,9.8542 -78.4604,24.07205 -31.505,15.77045 -56.5528,33.57164 -78.5866,55.85076 -22.9969,23.25282 -37.4392,45.78809 -46.737,72.92624 -3.3375,9.74165 -8.0905,29.93345 -7.2451,30.77886 2.3874,2.38739 64.9877,3.04398 84.317,0.88436 z"/>
|
|
</svg>
|
|
<div>
|
|
<span class="brand">cameleer</span><span class="version">v3</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="sidebar-search">
|
|
<div class="sidebar-search-wrap">
|
|
<span class="search-icon">◇</span>
|
|
<input type="text" placeholder="Filter applications...">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="sidebar-section">Applications</div>
|
|
|
|
<div class="sidebar-items">
|
|
<div class="sidebar-item">
|
|
<span class="health health-live"></span>
|
|
<div class="item-info"><div class="item-name">All Applications</div></div>
|
|
<span class="item-count">4</span>
|
|
</div>
|
|
|
|
<div class="sidebar-divider"></div>
|
|
|
|
<div class="sidebar-item">
|
|
<span class="health health-live"></span>
|
|
<div class="item-info">
|
|
<div class="item-name">order-service</div>
|
|
<div class="item-meta">2 agents</div>
|
|
</div>
|
|
<span class="item-count">1,847</span>
|
|
</div>
|
|
|
|
<div class="sidebar-divider"></div>
|
|
<div class="sidebar-section">Routes</div>
|
|
|
|
<div class="sidebar-item" style="padding-left: 22px;">
|
|
<span style="color: var(--sidebar-muted); font-size: 10px;">▸</span>
|
|
<div class="item-info"><div class="item-name">order-intake</div></div>
|
|
<span class="item-count">892</span>
|
|
</div>
|
|
|
|
<div class="sidebar-item active" style="padding-left: 22px;">
|
|
<span style="color: var(--amber-light); font-size: 10px;">▸</span>
|
|
<div class="item-info"><div class="item-name">order-processing</div></div>
|
|
<span class="item-count">541</span>
|
|
</div>
|
|
|
|
<div class="sidebar-item" style="padding-left: 22px;">
|
|
<span style="color: var(--sidebar-muted); font-size: 10px;">▸</span>
|
|
<div class="item-info"><div class="item-name">order-enrichment</div></div>
|
|
<span class="item-count">414</span>
|
|
</div>
|
|
|
|
<div class="sidebar-divider"></div>
|
|
|
|
<div class="sidebar-item">
|
|
<span class="health health-live"></span>
|
|
<div class="item-info">
|
|
<div class="item-name">payment-gateway</div>
|
|
<div class="item-meta">2 agents</div>
|
|
</div>
|
|
<span class="item-count">923</span>
|
|
</div>
|
|
|
|
<div class="sidebar-item">
|
|
<span class="health health-live"></span>
|
|
<div class="item-info">
|
|
<div class="item-name">shipment-tracker</div>
|
|
<div class="item-meta">2 agents</div>
|
|
</div>
|
|
<span class="item-count">471</span>
|
|
</div>
|
|
|
|
<div class="sidebar-item">
|
|
<span class="health health-stale"></span>
|
|
<div class="item-info">
|
|
<div class="item-name">notification-hub</div>
|
|
<div class="item-meta">1 agent</div>
|
|
</div>
|
|
<span class="item-count">128</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Agent health section -->
|
|
<div class="sidebar-agents-header">
|
|
<span>Agents</span>
|
|
<span class="sidebar-agent-badge">4/4 live</span>
|
|
</div>
|
|
|
|
<div style="padding: 0 0 6px; overflow-y: auto; max-height: 140px;">
|
|
<div class="agent-item">
|
|
<span class="agent-dot health-live"></span>
|
|
<div class="agent-info">
|
|
<div class="agent-name">prod-1</div>
|
|
<div class="agent-detail">order-service v3.2.1</div>
|
|
</div>
|
|
<div class="agent-stats-col">
|
|
<div class="agent-tps">14.2/s</div>
|
|
<div>12s ago</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="agent-item">
|
|
<span class="agent-dot health-live"></span>
|
|
<div class="agent-info">
|
|
<div class="agent-name">prod-2</div>
|
|
<div class="agent-detail">order-service v3.2.1</div>
|
|
</div>
|
|
<div class="agent-stats-col">
|
|
<div class="agent-tps">11.8/s</div>
|
|
<div style="color: var(--error);">3 err/h</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="sidebar-bottom">
|
|
<div class="sidebar-item"><span style="font-size: 13px; width: 18px; text-align: center;">⚙</span><div class="item-info"><div class="item-name">Admin</div></div></div>
|
|
<div class="sidebar-item"><span style="font-size: 13px; width: 18px; text-align: center;">☰</span><div class="item-info"><div class="item-name">API Docs</div></div></div>
|
|
</div>
|
|
</aside>
|
|
|
|
<!-- ====================================================================
|
|
MAIN CONTENT
|
|
==================================================================== -->
|
|
<div class="main">
|
|
<!-- Top bar -->
|
|
<div class="topbar">
|
|
<div class="topbar-breadcrumb">
|
|
<span class="crumb-link">Applications</span>
|
|
<span class="crumb-sep">/</span>
|
|
<span class="crumb-link">order-service</span>
|
|
<span class="crumb-sep">/</span>
|
|
<span class="crumb-active">order-processing</span>
|
|
</div>
|
|
|
|
<div class="topbar-search">
|
|
<span style="font-size: 12px; color: var(--text-faint);">🔎</span>
|
|
<span>Search by Order ID, route, error...</span>
|
|
<span class="topbar-kbd">Ctrl+K</span>
|
|
</div>
|
|
|
|
<div class="topbar-right">
|
|
<span class="topbar-env">PRODUCTION</span>
|
|
<span class="topbar-shift">Shift: Day (06:00-18:00)</span>
|
|
<div class="topbar-user">
|
|
<span>hendrik</span>
|
|
<div class="topbar-avatar">H</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Scrollable content -->
|
|
<div class="content">
|
|
|
|
<!-- ================================================================
|
|
ROUTE HEADER CARD
|
|
================================================================ -->
|
|
<div class="route-header animate-in">
|
|
<div class="route-header-main">
|
|
<div class="route-title-row">
|
|
<span class="route-title">order-processing</span>
|
|
<span class="route-status-badge route-status-started">
|
|
<span class="route-status-dot"></span>
|
|
Started
|
|
</span>
|
|
</div>
|
|
<div class="route-subtitle">
|
|
<span>CamelContext: <span class="mono">order-service-ctx</span></span>
|
|
<span class="dot-sep"></span>
|
|
<span>Uptime: <span class="mono">14d 6h 22m</span></span>
|
|
<span class="dot-sep"></span>
|
|
<span>Last restart: <span class="mono">Mar 3, 08:41 UTC</span></span>
|
|
<span class="dot-sep"></span>
|
|
<span>Version: <span class="mono">a3f8c2d</span></span>
|
|
</div>
|
|
</div>
|
|
<div class="route-header-stats">
|
|
<div class="route-stat">
|
|
<div class="route-stat-label">Throughput</div>
|
|
<div class="route-stat-value val-teal">12.4<span style="font-size: 11px; color: var(--text-muted); font-weight: 400;">/s</span></div>
|
|
<div class="route-stat-detail">peak 28.1/s</div>
|
|
</div>
|
|
<div class="route-stat">
|
|
<div class="route-stat-label">Inflight</div>
|
|
<div class="route-stat-value val-amber">3</div>
|
|
<div class="route-stat-detail">max 10</div>
|
|
</div>
|
|
<div class="route-stat">
|
|
<div class="route-stat-label">Success Rate</div>
|
|
<div class="route-stat-value val-green">96.8%</div>
|
|
<div class="route-stat-detail">523/541 ok</div>
|
|
</div>
|
|
<div class="route-stat">
|
|
<div class="route-stat-label">Errors (shift)</div>
|
|
<div class="route-stat-value val-error">18</div>
|
|
<div class="route-stat-detail">+5 since 06:00</div>
|
|
</div>
|
|
</div>
|
|
<div class="route-header-actions">
|
|
<button class="route-action-btn primary">Suspend Route</button>
|
|
<button class="route-action-btn secondary">View Config</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ================================================================
|
|
ROUTE DIAGRAM + PROCESSOR STATS (side by side)
|
|
================================================================ -->
|
|
<div class="diagram-stats-row animate-in delay-1">
|
|
|
|
<!-- Route Flow Diagram -->
|
|
<div class="diagram-card">
|
|
<div class="diagram-card-header">
|
|
<span class="diagram-card-title">Route Flow</span>
|
|
<div class="diagram-legend">
|
|
<div class="legend-item"><div class="legend-dot" style="background: var(--success);"></div> Healthy</div>
|
|
<div class="legend-item"><div class="legend-dot" style="background: var(--warning);"></div> Slow</div>
|
|
<div class="legend-item"><div class="legend-dot" style="background: var(--error);"></div> Errors</div>
|
|
</div>
|
|
</div>
|
|
<div class="diagram-body">
|
|
|
|
<!-- Node 1: from -->
|
|
<div class="diagram-node node-healthy">
|
|
<div class="node-icon icon-from">▶</div>
|
|
<div class="node-info">
|
|
<div class="node-type">from</div>
|
|
<div class="node-label">direct:order-intake</div>
|
|
</div>
|
|
<div class="node-stats">
|
|
<div class="node-duration dur-fast">2ms</div>
|
|
<div class="node-counts">12,841 / <span class="err-count">0</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="diagram-connector"><div class="connector-line"></div><div class="connector-arrow"></div></div>
|
|
|
|
<!-- Node 2: process -->
|
|
<div class="diagram-node node-healthy">
|
|
<div class="node-icon icon-process">⚙</div>
|
|
<div class="node-info">
|
|
<div class="node-type">process</div>
|
|
<div class="node-label">OrderValidator</div>
|
|
</div>
|
|
<div class="node-stats">
|
|
<div class="node-duration dur-fast">8ms</div>
|
|
<div class="node-counts">12,841 / <span class="err-count">12</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="diagram-connector"><div class="connector-line"></div><div class="connector-arrow"></div></div>
|
|
|
|
<!-- Node 3: to SQL -->
|
|
<div class="diagram-node node-healthy">
|
|
<div class="node-icon icon-to">▢</div>
|
|
<div class="node-info">
|
|
<div class="node-type">to</div>
|
|
<div class="node-label" title="sql:INSERT INTO orders...">sql:INSERT INTO orders...</div>
|
|
</div>
|
|
<div class="node-stats">
|
|
<div class="node-duration dur-normal">24ms</div>
|
|
<div class="node-counts">12,829 / <span class="err-count">3</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="diagram-connector"><div class="connector-line"></div><div class="connector-arrow"></div></div>
|
|
|
|
<!-- Node 4: choice -->
|
|
<div class="diagram-node node-healthy">
|
|
<div class="node-icon icon-choice">♦</div>
|
|
<div class="node-info">
|
|
<div class="node-type">choice</div>
|
|
<div class="node-label">header.priority == 'HIGH'</div>
|
|
</div>
|
|
<div class="node-stats">
|
|
<div class="node-duration dur-fast">1ms</div>
|
|
<div class="node-counts">12,826 / <span class="err-count">0</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="diagram-connector"><div class="connector-line"></div><div class="connector-arrow"></div></div>
|
|
|
|
<!-- Node 5: to payment-api — BOTTLENECK -->
|
|
<div class="diagram-node node-bottleneck">
|
|
<span class="bottleneck-badge">BOTTLENECK</span>
|
|
<div class="node-icon icon-to" style="background: var(--warning-bg); color: var(--warning);">⚡</div>
|
|
<div class="node-info">
|
|
<div class="node-type">to</div>
|
|
<div class="node-label">http:payment-api/charge</div>
|
|
</div>
|
|
<div class="node-stats">
|
|
<div class="node-duration dur-breach">187ms</div>
|
|
<div class="node-counts">4,218 / <span class="err-count">47</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="diagram-connector"><div class="connector-line"></div><div class="connector-arrow"></div></div>
|
|
|
|
<!-- Node 6: process ResponseMapper -->
|
|
<div class="diagram-node node-healthy">
|
|
<div class="node-icon icon-process">⚙</div>
|
|
<div class="node-info">
|
|
<div class="node-type">process</div>
|
|
<div class="node-label">ResponseMapper</div>
|
|
</div>
|
|
<div class="node-stats">
|
|
<div class="node-duration dur-fast">3ms</div>
|
|
<div class="node-counts">12,779 / <span class="err-count">0</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="diagram-connector"><div class="connector-line"></div><div class="connector-arrow"></div></div>
|
|
|
|
<!-- Node 7: to kafka -->
|
|
<div class="diagram-node node-healthy">
|
|
<div class="node-icon icon-to">▢</div>
|
|
<div class="node-info">
|
|
<div class="node-type">to</div>
|
|
<div class="node-label">kafka:order-completed</div>
|
|
</div>
|
|
<div class="node-stats">
|
|
<div class="node-duration dur-normal">11ms</div>
|
|
<div class="node-counts">12,779 / <span class="err-count">2</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Error handler section -->
|
|
<div class="diagram-error-section">
|
|
<div class="error-handler-label">Error Handler</div>
|
|
<div class="diagram-node node-error">
|
|
<div class="node-icon icon-error-handler">⚠</div>
|
|
<div class="node-info">
|
|
<div class="node-type">to</div>
|
|
<div class="node-label">dead-letter:failed-orders</div>
|
|
</div>
|
|
<div class="node-stats">
|
|
<div class="node-duration dur-normal">14ms</div>
|
|
<div class="node-counts">62 total</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Processor Statistics Table -->
|
|
<div class="stats-card">
|
|
<div class="stats-card-header">
|
|
<span class="stats-card-title">Processor Statistics</span>
|
|
<span class="stats-card-meta">Last 6 hours · 8 processors</span>
|
|
</div>
|
|
<div class="stats-table-wrap">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Processor</th>
|
|
<th>Type</th>
|
|
<th class="sorted">Avg Duration <span class="sort-arrow">▼</span></th>
|
|
<th>P99</th>
|
|
<th class="text-right">Invocations</th>
|
|
<th class="text-right">Errors</th>
|
|
<th class="text-right">Error %</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<!-- BOTTLENECK ROW: payment-api -->
|
|
<tr class="row-highlight">
|
|
<td><span class="proc-name-cell">http:payment-api/charge</span></td>
|
|
<td><span class="proc-type-badge proc-type-to">TO</span></td>
|
|
<td>
|
|
<div class="dur-bar-wrap">
|
|
<span class="duration-cell dur-breach">187ms</span>
|
|
<div class="dur-bar"><div class="dur-bar-fill fill-breach" style="width: 100%;"></div></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="duration-cell dur-breach">892ms</span></td>
|
|
<td class="text-right"><span class="invocations-cell">4,218</span></td>
|
|
<td class="text-right"><span class="duration-cell dur-breach">47</span></td>
|
|
<td class="text-right"><span class="error-pct high">1.11%</span></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><span class="proc-name-cell">sql:INSERT INTO orders...</span></td>
|
|
<td><span class="proc-type-badge proc-type-to">TO</span></td>
|
|
<td>
|
|
<div class="dur-bar-wrap">
|
|
<span class="duration-cell dur-normal">24ms</span>
|
|
<div class="dur-bar"><div class="dur-bar-fill fill-normal" style="width: 13%;"></div></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="duration-cell dur-slow">78ms</span></td>
|
|
<td class="text-right"><span class="invocations-cell">12,829</span></td>
|
|
<td class="text-right"><span class="duration-cell">3</span></td>
|
|
<td class="text-right"><span class="error-pct low">0.02%</span></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><span class="proc-name-cell">dead-letter:failed-orders</span></td>
|
|
<td><span class="proc-type-badge proc-type-error">DLQ</span></td>
|
|
<td>
|
|
<div class="dur-bar-wrap">
|
|
<span class="duration-cell dur-normal">14ms</span>
|
|
<div class="dur-bar"><div class="dur-bar-fill fill-normal" style="width: 7%;"></div></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="duration-cell dur-normal">31ms</span></td>
|
|
<td class="text-right"><span class="invocations-cell">62</span></td>
|
|
<td class="text-right"><span class="duration-cell">0</span></td>
|
|
<td class="text-right"><span class="error-pct low">0.00%</span></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><span class="proc-name-cell">kafka:order-completed</span></td>
|
|
<td><span class="proc-type-badge proc-type-to">TO</span></td>
|
|
<td>
|
|
<div class="dur-bar-wrap">
|
|
<span class="duration-cell dur-normal">11ms</span>
|
|
<div class="dur-bar"><div class="dur-bar-fill fill-normal" style="width: 6%;"></div></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="duration-cell dur-normal">42ms</span></td>
|
|
<td class="text-right"><span class="invocations-cell">12,779</span></td>
|
|
<td class="text-right"><span class="duration-cell">2</span></td>
|
|
<td class="text-right"><span class="error-pct low">0.02%</span></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><span class="proc-name-cell">OrderValidator</span></td>
|
|
<td><span class="proc-type-badge proc-type-process">PROC</span></td>
|
|
<td>
|
|
<div class="dur-bar-wrap">
|
|
<span class="duration-cell dur-fast">8ms</span>
|
|
<div class="dur-bar"><div class="dur-bar-fill fill-fast" style="width: 4%;"></div></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="duration-cell dur-normal">19ms</span></td>
|
|
<td class="text-right"><span class="invocations-cell">12,841</span></td>
|
|
<td class="text-right"><span class="duration-cell">12</span></td>
|
|
<td class="text-right"><span class="error-pct low">0.09%</span></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><span class="proc-name-cell">ResponseMapper</span></td>
|
|
<td><span class="proc-type-badge proc-type-process">PROC</span></td>
|
|
<td>
|
|
<div class="dur-bar-wrap">
|
|
<span class="duration-cell dur-fast">3ms</span>
|
|
<div class="dur-bar"><div class="dur-bar-fill fill-fast" style="width: 2%;"></div></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="duration-cell dur-fast">7ms</span></td>
|
|
<td class="text-right"><span class="invocations-cell">12,779</span></td>
|
|
<td class="text-right"><span class="duration-cell">0</span></td>
|
|
<td class="text-right"><span class="error-pct low">0.00%</span></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><span class="proc-name-cell">direct:order-intake</span></td>
|
|
<td><span class="proc-type-badge proc-type-from">FROM</span></td>
|
|
<td>
|
|
<div class="dur-bar-wrap">
|
|
<span class="duration-cell dur-fast">2ms</span>
|
|
<div class="dur-bar"><div class="dur-bar-fill fill-fast" style="width: 1%;"></div></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="duration-cell dur-fast">4ms</span></td>
|
|
<td class="text-right"><span class="invocations-cell">12,841</span></td>
|
|
<td class="text-right"><span class="duration-cell">0</span></td>
|
|
<td class="text-right"><span class="error-pct low">0.00%</span></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><span class="proc-name-cell">choice(priority)</span></td>
|
|
<td><span class="proc-type-badge proc-type-choice">CHOICE</span></td>
|
|
<td>
|
|
<div class="dur-bar-wrap">
|
|
<span class="duration-cell dur-fast">1ms</span>
|
|
<div class="dur-bar"><div class="dur-bar-fill fill-fast" style="width: 0.5%;"></div></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="duration-cell dur-fast">2ms</span></td>
|
|
<td class="text-right"><span class="invocations-cell">12,826</span></td>
|
|
<td class="text-right"><span class="duration-cell">0</span></td>
|
|
<td class="text-right"><span class="error-pct low">0.00%</span></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ================================================================
|
|
TABBED SECTION: Performance | Recent Executions | Error Patterns
|
|
================================================================ -->
|
|
<div class="tabs-section animate-in delay-3">
|
|
<div class="tabs-header">
|
|
<button class="tab-btn active" onclick="switchTab('performance')">Performance<span class="tab-count">6h</span></button>
|
|
<button class="tab-btn" onclick="switchTab('executions')">Recent Executions<span class="tab-count">20</span></button>
|
|
<button class="tab-btn" onclick="switchTab('errors')">Error Patterns<span class="tab-count">5</span></button>
|
|
</div>
|
|
|
|
<!-- ============================================================
|
|
TAB 1: Performance
|
|
============================================================ -->
|
|
<div class="tab-content active" id="tab-performance">
|
|
<div class="perf-summary">
|
|
<div class="perf-stat stat-throughput">
|
|
<div class="perf-stat-label">Throughput</div>
|
|
<div>
|
|
<span class="perf-stat-value val-teal">12.4</span>
|
|
<span class="perf-stat-unit">msg/s</span>
|
|
<span class="perf-stat-trend trend-up-good">▲ +8%</span>
|
|
</div>
|
|
<div class="perf-stat-detail">Peak: 28.1/s at 08:42</div>
|
|
</div>
|
|
|
|
<div class="perf-stat stat-error-rate">
|
|
<div class="perf-stat-label">Error Rate</div>
|
|
<div>
|
|
<span class="perf-stat-value val-error">3.2%</span>
|
|
<span class="perf-stat-trend trend-up-bad">▲ +0.8%</span>
|
|
</div>
|
|
<div class="perf-stat-detail">18 errors / 541 total</div>
|
|
</div>
|
|
|
|
<div class="perf-stat stat-avg-latency">
|
|
<div class="perf-stat-label">Avg Latency</div>
|
|
<div>
|
|
<span class="perf-stat-value val-amber">142</span>
|
|
<span class="perf-stat-unit">ms</span>
|
|
<span class="perf-stat-trend trend-up-bad">▲ +18ms</span>
|
|
</div>
|
|
<div class="perf-stat-detail">Median: 98ms</div>
|
|
</div>
|
|
|
|
<div class="perf-stat stat-p99">
|
|
<div class="perf-stat-label">P99 Latency</div>
|
|
<div>
|
|
<span class="perf-stat-value val-warn">487</span>
|
|
<span class="perf-stat-unit">ms</span>
|
|
<span class="perf-stat-trend trend-up-bad">▲ +52ms</span>
|
|
</div>
|
|
<div class="perf-stat-detail">SLA: <500ms · <strong style="color: var(--warning);">CLOSE</strong></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="charts-row">
|
|
<!-- Throughput chart -->
|
|
<div class="chart-panel">
|
|
<div class="chart-panel-title">
|
|
<span>Throughput (msg/s)</span>
|
|
<span class="chart-panel-value">12.4/s</span>
|
|
</div>
|
|
<div class="chart-area-wrapper">
|
|
<svg width="100%" height="100" viewBox="0 0 400 100" preserveAspectRatio="none">
|
|
<line x1="0" y1="25" x2="400" y2="25" class="chart-grid-line"/>
|
|
<line x1="0" y1="50" x2="400" y2="50" class="chart-grid-line"/>
|
|
<line x1="0" y1="75" x2="400" y2="75" class="chart-grid-line"/>
|
|
<text x="2" y="24" class="chart-label">30</text>
|
|
<text x="2" y="49" class="chart-label">20</text>
|
|
<text x="2" y="74" class="chart-label">10</text>
|
|
<text x="2" y="97" class="chart-label">0</text>
|
|
<path d="M20,78 L60,72 L100,65 L140,58 L180,45 L220,35 L260,42 L300,48 L340,52 L380,55" class="chart-line" stroke="#1A7F8E"/>
|
|
<path d="M20,78 L60,72 L100,65 L140,58 L180,45 L220,35 L260,42 L300,48 L340,52 L380,55 L380,100 L20,100 Z" class="chart-area" fill="#1A7F8E"/>
|
|
<text x="60" y="97" class="chart-label">04:00</text>
|
|
<text x="180" y="97" class="chart-label">06:00</text>
|
|
<text x="300" y="97" class="chart-label">08:00</text>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Latency distribution chart -->
|
|
<div class="chart-panel">
|
|
<div class="chart-panel-title">
|
|
<span>Latency Distribution</span>
|
|
<span class="chart-panel-value">avg 142ms</span>
|
|
</div>
|
|
<div class="chart-area-wrapper">
|
|
<svg width="100%" height="100" viewBox="0 0 400 100" preserveAspectRatio="none">
|
|
<line x1="0" y1="25" x2="400" y2="25" class="chart-grid-line"/>
|
|
<line x1="0" y1="50" x2="400" y2="50" class="chart-grid-line"/>
|
|
<line x1="0" y1="75" x2="400" y2="75" class="chart-grid-line"/>
|
|
<!-- Histogram bars -->
|
|
<rect x="30" y="15" width="30" height="80" rx="2" fill="#C6820E" opacity="0.15"/>
|
|
<rect x="65" y="25" width="30" height="70" rx="2" fill="#C6820E" opacity="0.2"/>
|
|
<rect x="100" y="8" width="30" height="87" rx="2" fill="#C6820E" opacity="0.25"/>
|
|
<rect x="135" y="20" width="30" height="75" rx="2" fill="#C6820E" opacity="0.3"/>
|
|
<rect x="170" y="35" width="30" height="60" rx="2" fill="#C6820E" opacity="0.35"/>
|
|
<rect x="205" y="50" width="30" height="45" rx="2" fill="#C6820E" opacity="0.3"/>
|
|
<rect x="240" y="60" width="30" height="35" rx="2" fill="#C6820E" opacity="0.25"/>
|
|
<rect x="275" y="72" width="30" height="23" rx="2" fill="#C6820E" opacity="0.2"/>
|
|
<rect x="310" y="80" width="30" height="15" rx="2" fill="#C0392B" opacity="0.3"/>
|
|
<rect x="345" y="85" width="30" height="10" rx="2" fill="#C0392B" opacity="0.3"/>
|
|
<!-- SLA line -->
|
|
<line x1="310" y1="0" x2="310" y2="95" class="sla-line"/>
|
|
<text x="312" y="10" class="sla-label-text">SLA 500ms</text>
|
|
<text x="30" y="97" class="chart-label">0ms</text>
|
|
<text x="170" y="97" class="chart-label">250ms</text>
|
|
<text x="310" y="97" class="chart-label">500ms</text>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Error rate trend chart -->
|
|
<div class="chart-panel">
|
|
<div class="chart-panel-title">
|
|
<span>Error Rate Trend</span>
|
|
<span class="chart-panel-value" style="color: var(--error);">3.2%</span>
|
|
</div>
|
|
<div class="chart-area-wrapper">
|
|
<svg width="100%" height="100" viewBox="0 0 400 100" preserveAspectRatio="none">
|
|
<line x1="0" y1="25" x2="400" y2="25" class="chart-grid-line"/>
|
|
<line x1="0" y1="50" x2="400" y2="50" class="chart-grid-line"/>
|
|
<line x1="0" y1="75" x2="400" y2="75" class="chart-grid-line"/>
|
|
<text x="2" y="24" class="chart-label">10%</text>
|
|
<text x="2" y="49" class="chart-label">5%</text>
|
|
<text x="2" y="74" class="chart-label">2%</text>
|
|
<text x="2" y="97" class="chart-label">0%</text>
|
|
<!-- SLA line at 5% -->
|
|
<line x1="20" y1="50" x2="380" y2="50" class="sla-line"/>
|
|
<text x="350" y="47" class="sla-label-text">SLA 5%</text>
|
|
<path d="M20,88 L60,85 L100,80 L140,82 L180,75 L220,70 L260,72 L300,68 L340,65 L380,62" class="chart-line" stroke="#C0392B"/>
|
|
<path d="M20,88 L60,85 L100,80 L140,82 L180,75 L220,70 L260,72 L300,68 L340,65 L380,62 L380,100 L20,100 Z" class="chart-area" fill="#C0392B"/>
|
|
<text x="60" y="97" class="chart-label">04:00</text>
|
|
<text x="180" y="97" class="chart-label">06:00</text>
|
|
<text x="300" y="97" class="chart-label">08:00</text>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ============================================================
|
|
TAB 2: Recent Executions
|
|
============================================================ -->
|
|
<div class="tab-content" id="tab-executions">
|
|
<div class="exec-table-header">
|
|
<span class="exec-table-meta">Showing 1-20 of 541 · This route only</span>
|
|
<button class="live-btn"><span class="live-dot"></span> LIVE</button>
|
|
</div>
|
|
<div class="exec-table-wrap">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th style="width: 80px;">Status</th>
|
|
<th>Business ID</th>
|
|
<th class="sorted">Started <span class="sort-arrow">▼</span></th>
|
|
<th>Duration</th>
|
|
<th>Processors</th>
|
|
<th>Agent</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr class="row-success">
|
|
<td><span class="status-badge status-success"><span class="status-dot"></span>OK</span></td>
|
|
<td><span class="biz-id">OP-92184</span></td>
|
|
<td><span class="time-relative">2 min ago</span><span class="time-absolute">09:12:04</span></td>
|
|
<td><span class="duration-cell dur-fast">87ms</span></td>
|
|
<td><span class="mono" style="font-size: 12px; color: var(--text-muted);">7/7</span></td>
|
|
<td><span class="mono" style="font-size: 12px; color: var(--text-muted);">prod-1</span></td>
|
|
</tr>
|
|
<tr class="row-success">
|
|
<td><span class="status-badge status-success"><span class="status-dot"></span>OK</span></td>
|
|
<td><span class="biz-id">OP-92183</span></td>
|
|
<td><span class="time-relative">3 min ago</span><span class="time-absolute">09:11:22</span></td>
|
|
<td><span class="duration-cell dur-normal">164ms</span></td>
|
|
<td><span class="mono" style="font-size: 12px; color: var(--text-muted);">7/7</span></td>
|
|
<td><span class="mono" style="font-size: 12px; color: var(--text-muted);">prod-2</span></td>
|
|
</tr>
|
|
<tr class="row-running">
|
|
<td><span class="status-badge status-running"><span class="status-dot" style="animation: pulse 1.5s ease-in-out infinite;"></span>RUN</span></td>
|
|
<td><span class="biz-id">OP-92185</span></td>
|
|
<td><span class="time-relative">30s ago</span><span class="time-absolute">09:13:44</span></td>
|
|
<td><span class="duration-cell" style="color: var(--running);">~30s</span></td>
|
|
<td><span class="mono" style="font-size: 12px; color: var(--running);">5/7</span></td>
|
|
<td><span class="mono" style="font-size: 12px; color: var(--text-muted);">prod-1</span></td>
|
|
</tr>
|
|
<tr class="row-error">
|
|
<td><span class="status-badge status-error"><span class="status-dot"></span>ERR</span></td>
|
|
<td><span class="biz-id">OP-92181</span></td>
|
|
<td><span class="time-relative">5 min ago</span><span class="time-absolute">09:09:12</span></td>
|
|
<td><span class="duration-cell dur-breach">412ms</span></td>
|
|
<td><span class="mono" style="font-size: 12px; color: var(--error);">5/7</span></td>
|
|
<td><span class="mono" style="font-size: 12px; color: var(--text-muted);">prod-2</span></td>
|
|
</tr>
|
|
<tr class="row-success">
|
|
<td><span class="status-badge status-success"><span class="status-dot"></span>OK</span></td>
|
|
<td><span class="biz-id">OP-92180</span></td>
|
|
<td><span class="time-relative">6 min ago</span><span class="time-absolute">09:08:47</span></td>
|
|
<td><span class="duration-cell dur-fast">91ms</span></td>
|
|
<td><span class="mono" style="font-size: 12px; color: var(--text-muted);">7/7</span></td>
|
|
<td><span class="mono" style="font-size: 12px; color: var(--text-muted);">prod-1</span></td>
|
|
</tr>
|
|
<tr class="row-warning">
|
|
<td><span class="status-badge status-warning"><span class="status-dot"></span>WARN</span></td>
|
|
<td><span class="biz-id">OP-92179</span></td>
|
|
<td><span class="time-relative">8 min ago</span><span class="time-absolute">09:06:33</span></td>
|
|
<td><span class="duration-cell dur-slow">348ms</span></td>
|
|
<td><span class="mono" style="font-size: 12px; color: var(--text-muted);">7/7</span></td>
|
|
<td><span class="mono" style="font-size: 12px; color: var(--text-muted);">prod-2</span></td>
|
|
</tr>
|
|
<tr class="row-success">
|
|
<td><span class="status-badge status-success"><span class="status-dot"></span>OK</span></td>
|
|
<td><span class="biz-id">OP-92178</span></td>
|
|
<td><span class="time-relative">9 min ago</span><span class="time-absolute">09:05:11</span></td>
|
|
<td><span class="duration-cell dur-fast">72ms</span></td>
|
|
<td><span class="mono" style="font-size: 12px; color: var(--text-muted);">7/7</span></td>
|
|
<td><span class="mono" style="font-size: 12px; color: var(--text-muted);">prod-1</span></td>
|
|
</tr>
|
|
<tr class="row-success">
|
|
<td><span class="status-badge status-success"><span class="status-dot"></span>OK</span></td>
|
|
<td><span class="biz-id">OP-92177</span></td>
|
|
<td><span class="time-relative">11 min ago</span><span class="time-absolute">09:03:29</span></td>
|
|
<td><span class="duration-cell dur-normal">134ms</span></td>
|
|
<td><span class="mono" style="font-size: 12px; color: var(--text-muted);">7/7</span></td>
|
|
<td><span class="mono" style="font-size: 12px; color: var(--text-muted);">prod-2</span></td>
|
|
</tr>
|
|
<tr class="row-error">
|
|
<td><span class="status-badge status-error"><span class="status-dot"></span>ERR</span></td>
|
|
<td><span class="biz-id">OP-92174</span></td>
|
|
<td><span class="time-relative">14 min ago</span><span class="time-absolute">09:00:02</span></td>
|
|
<td><span class="duration-cell dur-breach">5,041ms</span></td>
|
|
<td><span class="mono" style="font-size: 12px; color: var(--error);">5/7</span></td>
|
|
<td><span class="mono" style="font-size: 12px; color: var(--text-muted);">prod-1</span></td>
|
|
</tr>
|
|
<tr class="row-success">
|
|
<td><span class="status-badge status-success"><span class="status-dot"></span>OK</span></td>
|
|
<td><span class="biz-id">OP-92173</span></td>
|
|
<td><span class="time-relative">16 min ago</span><span class="time-absolute">08:58:44</span></td>
|
|
<td><span class="duration-cell dur-fast">68ms</span></td>
|
|
<td><span class="mono" style="font-size: 12px; color: var(--text-muted);">7/7</span></td>
|
|
<td><span class="mono" style="font-size: 12px; color: var(--text-muted);">prod-2</span></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ============================================================
|
|
TAB 3: Error Patterns
|
|
============================================================ -->
|
|
<div class="tab-content" id="tab-errors">
|
|
<div class="error-patterns">
|
|
|
|
<!-- Error Pattern 1 -->
|
|
<div class="error-pattern">
|
|
<div class="error-pattern-count">
|
|
<div class="error-count-num">47</div>
|
|
<div class="error-count-label">times</div>
|
|
</div>
|
|
<div class="error-pattern-info">
|
|
<div class="error-exception-class">java.net.http.HttpConnectTimeoutException</div>
|
|
<div class="error-message-preview">HTTP connect timed out after 5000ms — POST https://payment-api.internal/v2/charge. Connection pool exhausted, all 20 connections in use. Retry exhausted (3/3 attempts).</div>
|
|
<div class="error-pattern-meta">
|
|
<span>Processor: <span class="mono">http:payment-api/charge</span></span>
|
|
<span>Last seen: <span class="mono">2 min ago</span></span>
|
|
<span>First seen: <span class="mono">03:14 today</span></span>
|
|
</div>
|
|
</div>
|
|
<div class="error-trend-bar" title="Last 6 hours, hourly buckets">
|
|
<div class="error-trend-bar-segment" style="height: 4px;"></div>
|
|
<div class="error-trend-bar-segment" style="height: 6px;"></div>
|
|
<div class="error-trend-bar-segment" style="height: 8px;"></div>
|
|
<div class="error-trend-bar-segment active" style="height: 14px;"></div>
|
|
<div class="error-trend-bar-segment active" style="height: 18px;"></div>
|
|
<div class="error-trend-bar-segment active" style="height: 24px;"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Error Pattern 2 -->
|
|
<div class="error-pattern">
|
|
<div class="error-pattern-count">
|
|
<div class="error-count-num">12</div>
|
|
<div class="error-count-label">times</div>
|
|
</div>
|
|
<div class="error-pattern-info">
|
|
<div class="error-exception-class">com.cameleer.validation.OrderValidationException</div>
|
|
<div class="error-message-preview">Order validation failed: required field 'shippingAddress.postalCode' is null or empty. Validation rule: ADDR-001. Source system: EDI-US.</div>
|
|
<div class="error-pattern-meta">
|
|
<span>Processor: <span class="mono">OrderValidator</span></span>
|
|
<span>Last seen: <span class="mono">18 min ago</span></span>
|
|
<span>First seen: <span class="mono">yesterday 22:07</span></span>
|
|
</div>
|
|
</div>
|
|
<div class="error-trend-bar" title="Last 6 hours, hourly buckets">
|
|
<div class="error-trend-bar-segment" style="height: 8px;"></div>
|
|
<div class="error-trend-bar-segment active" style="height: 12px;"></div>
|
|
<div class="error-trend-bar-segment" style="height: 6px;"></div>
|
|
<div class="error-trend-bar-segment" style="height: 4px;"></div>
|
|
<div class="error-trend-bar-segment active" style="height: 10px;"></div>
|
|
<div class="error-trend-bar-segment" style="height: 6px;"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Error Pattern 3 -->
|
|
<div class="error-pattern">
|
|
<div class="error-pattern-count">
|
|
<div class="error-count-num">3</div>
|
|
<div class="error-count-label">times</div>
|
|
</div>
|
|
<div class="error-pattern-info">
|
|
<div class="error-exception-class">java.sql.SQLTransientConnectionException</div>
|
|
<div class="error-message-preview">HikariPool-1 — Connection not available, request timed out after 1000ms. Pool stats: total=10, active=10, idle=0, waiting=3.</div>
|
|
<div class="error-pattern-meta">
|
|
<span>Processor: <span class="mono">sql:INSERT INTO orders...</span></span>
|
|
<span>Last seen: <span class="mono">41 min ago</span></span>
|
|
<span>First seen: <span class="mono">today 07:22</span></span>
|
|
</div>
|
|
</div>
|
|
<div class="error-trend-bar" title="Last 6 hours, hourly buckets">
|
|
<div class="error-trend-bar-segment" style="height: 0px;"></div>
|
|
<div class="error-trend-bar-segment" style="height: 0px;"></div>
|
|
<div class="error-trend-bar-segment" style="height: 4px;"></div>
|
|
<div class="error-trend-bar-segment active" style="height: 10px;"></div>
|
|
<div class="error-trend-bar-segment" style="height: 6px;"></div>
|
|
<div class="error-trend-bar-segment" style="height: 0px;"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Error Pattern 4 -->
|
|
<div class="error-pattern">
|
|
<div class="error-pattern-count">
|
|
<div class="error-count-num">2</div>
|
|
<div class="error-count-label">times</div>
|
|
</div>
|
|
<div class="error-pattern-info">
|
|
<div class="error-exception-class">org.apache.kafka.common.errors.TimeoutException</div>
|
|
<div class="error-message-preview">Expiring 1 record(s) for order-completed-0: 30004 ms has passed since batch creation. Topic partition: order-completed-0, broker: kafka-0.kafka.svc:9092.</div>
|
|
<div class="error-pattern-meta">
|
|
<span>Processor: <span class="mono">kafka:order-completed</span></span>
|
|
<span>Last seen: <span class="mono">2h ago</span></span>
|
|
<span>First seen: <span class="mono">today 04:11</span></span>
|
|
</div>
|
|
</div>
|
|
<div class="error-trend-bar" title="Last 6 hours, hourly buckets">
|
|
<div class="error-trend-bar-segment" style="height: 6px;"></div>
|
|
<div class="error-trend-bar-segment active" style="height: 8px;"></div>
|
|
<div class="error-trend-bar-segment" style="height: 0px;"></div>
|
|
<div class="error-trend-bar-segment" style="height: 0px;"></div>
|
|
<div class="error-trend-bar-segment" style="height: 0px;"></div>
|
|
<div class="error-trend-bar-segment" style="height: 0px;"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Error Pattern 5 -->
|
|
<div class="error-pattern">
|
|
<div class="error-pattern-count">
|
|
<div class="error-count-num">1</div>
|
|
<div class="error-count-label">times</div>
|
|
</div>
|
|
<div class="error-pattern-info">
|
|
<div class="error-exception-class">com.fasterxml.jackson.databind.JsonMappingException</div>
|
|
<div class="error-message-preview">Cannot deserialize value of type `java.math.BigDecimal` from String "N/A": not a valid representation at [Source: (byte[]); line: 1, column: 847] (through reference chain: Order["totalAmount"]).</div>
|
|
<div class="error-pattern-meta">
|
|
<span>Processor: <span class="mono">ResponseMapper</span></span>
|
|
<span>Last seen: <span class="mono">5h ago</span></span>
|
|
<span>First seen: <span class="mono">today 04:33</span></span>
|
|
</div>
|
|
</div>
|
|
<div class="error-trend-bar" title="Last 6 hours, hourly buckets">
|
|
<div class="error-trend-bar-segment active" style="height: 6px;"></div>
|
|
<div class="error-trend-bar-segment" style="height: 0px;"></div>
|
|
<div class="error-trend-bar-segment" style="height: 0px;"></div>
|
|
<div class="error-trend-bar-segment" style="height: 0px;"></div>
|
|
<div class="error-trend-bar-segment" style="height: 0px;"></div>
|
|
<div class="error-trend-bar-segment" style="height: 0px;"></div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
</div><!-- /tabs-section -->
|
|
|
|
</div><!-- /content -->
|
|
</div><!-- /main -->
|
|
|
|
</div><!-- /app -->
|
|
|
|
<!-- Keyboard shortcuts bar -->
|
|
<div class="shortcuts-bar">
|
|
<div class="shortcut-hint"><kbd>Esc</kbd> Back to list</div>
|
|
<div class="shortcut-hint"><kbd>R</kbd> Refresh</div>
|
|
<div class="shortcut-hint"><kbd>S</kbd> Suspend route</div>
|
|
<div class="shortcut-hint"><kbd>1</kbd><kbd>2</kbd><kbd>3</kbd> Switch tabs</div>
|
|
</div>
|
|
|
|
<script>
|
|
function switchTab(tabId) {
|
|
// Deactivate all tabs and content
|
|
document.querySelectorAll('.tab-btn').forEach(function(btn) { btn.classList.remove('active'); });
|
|
document.querySelectorAll('.tab-content').forEach(function(tc) { tc.classList.remove('active'); });
|
|
|
|
// Activate the selected tab
|
|
var tabMap = { 'performance': 0, 'executions': 1, 'errors': 2 };
|
|
var idx = tabMap[tabId];
|
|
document.querySelectorAll('.tab-btn')[idx].classList.add('active');
|
|
document.getElementById('tab-' + tabId).classList.add('active');
|
|
}
|
|
|
|
// Keyboard shortcuts
|
|
document.addEventListener('keydown', function(e) {
|
|
if (e.target.tagName === 'INPUT') return;
|
|
if (e.key === '1') switchTab('performance');
|
|
if (e.key === '2') switchTab('executions');
|
|
if (e.key === '3') switchTab('errors');
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|