2178 lines
91 KiB
HTML
2178 lines
91 KiB
HTML
|
|
<!--
|
||
|
|
============================================================================
|
||
|
|
CAMELEER3 v3 — Metrics & KPI Dashboard (Light Theme)
|
||
|
|
============================================================================
|
||
|
|
|
||
|
|
DESIGN NOTES
|
||
|
|
============
|
||
|
|
|
||
|
|
PURPOSE:
|
||
|
|
A dedicated KPI/Metrics page focused on system-wide and per-route performance
|
||
|
|
metrics. Separate from the transaction list (v2), this page provides at-a-glance
|
||
|
|
system health, per-route performance comparison, and time-series trend analysis.
|
||
|
|
|
||
|
|
LAYOUT:
|
||
|
|
- Same 220px warm charcoal sidebar as v2 (with "Metrics" active)
|
||
|
|
- Same 48px topbar with breadcrumb: Dashboard > Metrics
|
||
|
|
- Scrollable main content with:
|
||
|
|
1. Time range selector bar + auto-refresh indicator
|
||
|
|
2. System Health KPI cards (5 large cards)
|
||
|
|
3. Per-Route Performance Table with sparklines
|
||
|
|
4. 2x2 Time-Series Chart grid (SVG, no libraries)
|
||
|
|
5. Live Event Feed panel (right rail, optional)
|
||
|
|
|
||
|
|
DESIGN SYSTEM (identical to v2-light):
|
||
|
|
- Surface: warm parchment #F5F2ED, cards #FFFFFF
|
||
|
|
- Sidebar: warm charcoal #2C2520
|
||
|
|
- Brand accent: amber-gold #C6820E
|
||
|
|
- Typography: DM Sans body + JetBrains Mono data
|
||
|
|
- Status: olive green #3D7C47, burnt orange #C27516, terracotta #C0392B, teal #1A7F8E
|
||
|
|
- Shadows, radii, border styles all from v2
|
||
|
|
|
||
|
|
DATA STORY:
|
||
|
|
- order-processing: high throughput, low latency, healthy
|
||
|
|
- payment-flow: moderate throughput, HIGHER latency (payment gateway), occasional errors
|
||
|
|
- shipment-notify: steady, fast
|
||
|
|
- inventory-check: fast, low volume
|
||
|
|
- customer-validation: moderate, stable
|
||
|
|
- email-notification: low volume, very fast
|
||
|
|
- retry-handler: LOW throughput but HIGH error rate (by design, retries failed messages)
|
||
|
|
- dead-letter-processor: very low volume, 100% "errors" (receives poison pills)
|
||
|
|
|
||
|
|
CHARTS:
|
||
|
|
All SVG inline, no external libraries. Sparklines in KPI cards and table.
|
||
|
|
Four large charts: throughput area, latency lines, error bars, volume heatmap.
|
||
|
|
|
||
|
|
============================================================================
|
||
|
|
-->
|
||
|
|
<!DOCTYPE html>
|
||
|
|
<html lang="en">
|
||
|
|
<head>
|
||
|
|
<meta charset="UTF-8">
|
||
|
|
<meta name="viewport" content="width=1920">
|
||
|
|
<title>Cameleer3 — Metrics Dashboard</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);
|
||
|
|
|
||
|
|
/* Chart palette (warm spectrum) */
|
||
|
|
--chart-1: #C6820E; /* amber */
|
||
|
|
--chart-2: #3D7C47; /* olive green */
|
||
|
|
--chart-3: #1A7F8E; /* teal */
|
||
|
|
--chart-4: #C27516; /* burnt orange */
|
||
|
|
--chart-5: #8B5A06; /* deep amber */
|
||
|
|
--chart-6: #6B8E4E; /* sage */
|
||
|
|
--chart-7: #C0392B; /* terracotta */
|
||
|
|
--chart-8: #9C7A3C; /* gold */
|
||
|
|
}
|
||
|
|
|
||
|
|
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 spin {
|
||
|
|
from { transform: rotate(0deg); }
|
||
|
|
to { transform: rotate(360deg); }
|
||
|
|
}
|
||
|
|
.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 structure 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); }
|
||
|
|
.health-dead { background: var(--sidebar-muted); }
|
||
|
|
|
||
|
|
.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;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Agent health in sidebar */
|
||
|
|
.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-active { color: var(--text-primary); font-weight: 600; }
|
||
|
|
.topbar-breadcrumb .crumb-sep { color: var(--text-faint); font-size: 11px; }
|
||
|
|
|
||
|
|
.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;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ==========================================================================
|
||
|
|
TIME RANGE SELECTOR BAR
|
||
|
|
========================================================================== */
|
||
|
|
.time-range-bar {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: space-between;
|
||
|
|
margin-bottom: 16px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.time-range-pills {
|
||
|
|
display: flex;
|
||
|
|
gap: 4px;
|
||
|
|
background: var(--bg-surface);
|
||
|
|
border: 1px solid var(--border-subtle);
|
||
|
|
border-radius: var(--radius-md);
|
||
|
|
padding: 3px;
|
||
|
|
box-shadow: var(--shadow-card);
|
||
|
|
}
|
||
|
|
|
||
|
|
.time-pill {
|
||
|
|
padding: 5px 14px;
|
||
|
|
border-radius: var(--radius-sm);
|
||
|
|
font-size: 12px;
|
||
|
|
font-weight: 500;
|
||
|
|
color: var(--text-secondary);
|
||
|
|
cursor: pointer;
|
||
|
|
transition: all 0.12s;
|
||
|
|
background: none;
|
||
|
|
border: none;
|
||
|
|
font-family: var(--font-body);
|
||
|
|
}
|
||
|
|
|
||
|
|
.time-pill:hover { background: var(--bg-hover); }
|
||
|
|
.time-pill.active {
|
||
|
|
background: var(--amber);
|
||
|
|
color: white;
|
||
|
|
font-weight: 600;
|
||
|
|
}
|
||
|
|
|
||
|
|
.refresh-group {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 10px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.auto-refresh {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 6px;
|
||
|
|
font-size: 11px;
|
||
|
|
color: var(--text-muted);
|
||
|
|
font-family: var(--font-mono);
|
||
|
|
}
|
||
|
|
|
||
|
|
.auto-refresh-dot {
|
||
|
|
width: 6px;
|
||
|
|
height: 6px;
|
||
|
|
border-radius: 50%;
|
||
|
|
background: var(--success);
|
||
|
|
animation: pulse 2s ease-in-out infinite;
|
||
|
|
}
|
||
|
|
|
||
|
|
.refresh-btn {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 5px;
|
||
|
|
padding: 5px 12px;
|
||
|
|
border: 1px solid var(--border);
|
||
|
|
border-radius: var(--radius-sm);
|
||
|
|
background: var(--bg-surface);
|
||
|
|
color: var(--text-secondary);
|
||
|
|
font-size: 11px;
|
||
|
|
font-family: var(--font-body);
|
||
|
|
cursor: pointer;
|
||
|
|
transition: all 0.15s;
|
||
|
|
box-shadow: var(--shadow-sm);
|
||
|
|
}
|
||
|
|
|
||
|
|
.refresh-btn:hover { border-color: var(--text-faint); background: var(--bg-raised); }
|
||
|
|
|
||
|
|
.refresh-icon {
|
||
|
|
display: inline-block;
|
||
|
|
font-size: 12px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.last-updated {
|
||
|
|
font-size: 10px;
|
||
|
|
color: var(--text-faint);
|
||
|
|
font-family: var(--font-mono);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ==========================================================================
|
||
|
|
KPI CARDS (System Health Overview)
|
||
|
|
========================================================================== */
|
||
|
|
.kpi-strip {
|
||
|
|
display: grid;
|
||
|
|
grid-template-columns: repeat(5, 1fr);
|
||
|
|
gap: 12px;
|
||
|
|
margin-bottom: 20px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.kpi-card {
|
||
|
|
background: var(--bg-surface);
|
||
|
|
border: 1px solid var(--border-subtle);
|
||
|
|
border-radius: var(--radius-lg);
|
||
|
|
padding: 16px 18px 12px;
|
||
|
|
box-shadow: var(--shadow-card);
|
||
|
|
position: relative;
|
||
|
|
overflow: hidden;
|
||
|
|
transition: box-shadow 0.15s;
|
||
|
|
cursor: pointer;
|
||
|
|
}
|
||
|
|
|
||
|
|
.kpi-card:hover { box-shadow: var(--shadow-md); }
|
||
|
|
|
||
|
|
.kpi-card::before {
|
||
|
|
content: '';
|
||
|
|
position: absolute;
|
||
|
|
top: 0;
|
||
|
|
left: 0;
|
||
|
|
right: 0;
|
||
|
|
height: 3px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.kpi-card.card-amber::before { background: linear-gradient(90deg, var(--amber), transparent); }
|
||
|
|
.kpi-card.card-green::before { background: linear-gradient(90deg, var(--success), transparent); }
|
||
|
|
.kpi-card.card-error::before { background: linear-gradient(90deg, var(--error), transparent); }
|
||
|
|
.kpi-card.card-teal::before { background: linear-gradient(90deg, var(--running), transparent); }
|
||
|
|
.kpi-card.card-warn::before { background: linear-gradient(90deg, var(--warning), transparent); }
|
||
|
|
|
||
|
|
.kpi-label {
|
||
|
|
font-size: 10px;
|
||
|
|
font-weight: 600;
|
||
|
|
text-transform: uppercase;
|
||
|
|
letter-spacing: 0.6px;
|
||
|
|
color: var(--text-muted);
|
||
|
|
margin-bottom: 6px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.kpi-value-row {
|
||
|
|
display: flex;
|
||
|
|
align-items: baseline;
|
||
|
|
gap: 6px;
|
||
|
|
margin-bottom: 4px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.kpi-value {
|
||
|
|
font-family: var(--font-mono);
|
||
|
|
font-size: 26px;
|
||
|
|
font-weight: 600;
|
||
|
|
line-height: 1.2;
|
||
|
|
}
|
||
|
|
|
||
|
|
.kpi-value.val-amber { color: var(--amber); }
|
||
|
|
.kpi-value.val-green { color: var(--success); }
|
||
|
|
.kpi-value.val-error { color: var(--error); }
|
||
|
|
.kpi-value.val-teal { color: var(--running); }
|
||
|
|
.kpi-value.val-warn { color: var(--warning); }
|
||
|
|
|
||
|
|
.kpi-unit {
|
||
|
|
font-size: 12px;
|
||
|
|
color: var(--text-muted);
|
||
|
|
}
|
||
|
|
|
||
|
|
.kpi-trend {
|
||
|
|
font-family: var(--font-mono);
|
||
|
|
font-size: 11px;
|
||
|
|
display: inline-flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 2px;
|
||
|
|
margin-left: auto;
|
||
|
|
}
|
||
|
|
|
||
|
|
.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); }
|
||
|
|
|
||
|
|
.kpi-detail {
|
||
|
|
font-size: 11px;
|
||
|
|
color: var(--text-muted);
|
||
|
|
margin-top: 2px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.kpi-detail strong { color: var(--text-secondary); }
|
||
|
|
|
||
|
|
.kpi-sparkline {
|
||
|
|
margin-top: 8px;
|
||
|
|
height: 32px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.kpi-sparkline svg { width: 100%; height: 100%; }
|
||
|
|
|
||
|
|
/* Latency sub-values */
|
||
|
|
.latency-values {
|
||
|
|
display: flex;
|
||
|
|
gap: 12px;
|
||
|
|
margin-top: 4px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.latency-item {
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
align-items: center;
|
||
|
|
}
|
||
|
|
|
||
|
|
.latency-label {
|
||
|
|
font-size: 9px;
|
||
|
|
font-weight: 600;
|
||
|
|
text-transform: uppercase;
|
||
|
|
color: var(--text-faint);
|
||
|
|
letter-spacing: 0.5px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.latency-val {
|
||
|
|
font-family: var(--font-mono);
|
||
|
|
font-size: 16px;
|
||
|
|
font-weight: 600;
|
||
|
|
color: var(--text-primary);
|
||
|
|
}
|
||
|
|
|
||
|
|
.latency-val.lat-green { color: var(--success); }
|
||
|
|
.latency-val.lat-amber { color: var(--warning); }
|
||
|
|
.latency-val.lat-red { color: var(--error); }
|
||
|
|
|
||
|
|
.latency-trend {
|
||
|
|
font-family: var(--font-mono);
|
||
|
|
font-size: 9px;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Mini donut */
|
||
|
|
.mini-donut-wrap {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 12px;
|
||
|
|
margin-top: 6px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.mini-donut { position: relative; width: 40px; height: 40px; }
|
||
|
|
|
||
|
|
.donut-label {
|
||
|
|
position: absolute;
|
||
|
|
top: 50%;
|
||
|
|
left: 50%;
|
||
|
|
transform: translate(-50%, -50%);
|
||
|
|
font-family: var(--font-mono);
|
||
|
|
font-size: 9px;
|
||
|
|
font-weight: 600;
|
||
|
|
color: var(--text-secondary);
|
||
|
|
}
|
||
|
|
|
||
|
|
.donut-legend {
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
gap: 2px;
|
||
|
|
font-size: 10px;
|
||
|
|
color: var(--text-muted);
|
||
|
|
font-family: var(--font-mono);
|
||
|
|
}
|
||
|
|
|
||
|
|
.donut-legend-active { color: var(--success); }
|
||
|
|
|
||
|
|
/* ==========================================================================
|
||
|
|
PER-ROUTE PERFORMANCE TABLE
|
||
|
|
========================================================================== */
|
||
|
|
.table-section {
|
||
|
|
background: var(--bg-surface);
|
||
|
|
border: 1px solid var(--border-subtle);
|
||
|
|
border-radius: var(--radius-lg);
|
||
|
|
box-shadow: var(--shadow-card);
|
||
|
|
overflow: hidden;
|
||
|
|
margin-bottom: 20px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.table-header {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: space-between;
|
||
|
|
padding: 10px 16px;
|
||
|
|
border-bottom: 1px solid var(--border-subtle);
|
||
|
|
}
|
||
|
|
|
||
|
|
.table-title { font-size: 13px; font-weight: 600; color: var(--text-primary); }
|
||
|
|
|
||
|
|
.table-right {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 12px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.table-meta { font-family: var(--font-mono); font-size: 11px; color: var(--text-muted); }
|
||
|
|
|
||
|
|
.table-scroll { overflow-x: auto; }
|
||
|
|
|
||
|
|
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; }
|
||
|
|
|
||
|
|
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); }
|
||
|
|
|
||
|
|
td {
|
||
|
|
padding: 9px 14px;
|
||
|
|
font-size: 13px;
|
||
|
|
vertical-align: middle;
|
||
|
|
white-space: nowrap;
|
||
|
|
}
|
||
|
|
|
||
|
|
.route-name-cell {
|
||
|
|
font-weight: 500;
|
||
|
|
color: var(--text-primary);
|
||
|
|
}
|
||
|
|
|
||
|
|
.route-name-cell:hover { color: var(--amber); }
|
||
|
|
|
||
|
|
.route-group-cell {
|
||
|
|
font-size: 11px;
|
||
|
|
color: var(--text-muted);
|
||
|
|
}
|
||
|
|
|
||
|
|
.mono { font-family: var(--font-mono); }
|
||
|
|
|
||
|
|
.cell-sparkline { display: inline-block; vertical-align: middle; }
|
||
|
|
|
||
|
|
/* Latency cell color coding */
|
||
|
|
.lat-cell { font-family: var(--font-mono); font-size: 12px; font-weight: 500; }
|
||
|
|
.lat-fast { color: var(--success); }
|
||
|
|
.lat-normal { color: var(--text-primary); }
|
||
|
|
.lat-slow { color: var(--warning); }
|
||
|
|
.lat-breach { color: var(--error); font-weight: 600; }
|
||
|
|
|
||
|
|
/* Error rate cell */
|
||
|
|
.err-rate { font-family: var(--font-mono); font-size: 12px; }
|
||
|
|
.err-low { color: var(--success); }
|
||
|
|
.err-mid { color: var(--warning); }
|
||
|
|
.err-high { color: var(--error); font-weight: 600; }
|
||
|
|
|
||
|
|
/* Status badge */
|
||
|
|
.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-dot {
|
||
|
|
width: 5px;
|
||
|
|
height: 5px;
|
||
|
|
border-radius: 50%;
|
||
|
|
background: currentColor;
|
||
|
|
}
|
||
|
|
|
||
|
|
.drill-arrow {
|
||
|
|
color: var(--text-faint);
|
||
|
|
font-size: 11px;
|
||
|
|
transition: color 0.12s;
|
||
|
|
}
|
||
|
|
|
||
|
|
tbody tr:hover .drill-arrow { color: var(--amber); }
|
||
|
|
|
||
|
|
/* ==========================================================================
|
||
|
|
CHART GRID (2x2)
|
||
|
|
========================================================================== */
|
||
|
|
.chart-grid {
|
||
|
|
display: grid;
|
||
|
|
grid-template-columns: 1fr 1fr;
|
||
|
|
gap: 14px;
|
||
|
|
margin-bottom: 20px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.chart-card {
|
||
|
|
background: var(--bg-surface);
|
||
|
|
border: 1px solid var(--border-subtle);
|
||
|
|
border-radius: var(--radius-lg);
|
||
|
|
box-shadow: var(--shadow-card);
|
||
|
|
overflow: hidden;
|
||
|
|
}
|
||
|
|
|
||
|
|
.chart-card-header {
|
||
|
|
padding: 12px 16px;
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: space-between;
|
||
|
|
border-bottom: 1px solid var(--border-subtle);
|
||
|
|
}
|
||
|
|
|
||
|
|
.chart-card-title {
|
||
|
|
font-size: 12px;
|
||
|
|
font-weight: 600;
|
||
|
|
color: var(--text-primary);
|
||
|
|
}
|
||
|
|
|
||
|
|
.chart-card-subtitle {
|
||
|
|
font-size: 10px;
|
||
|
|
color: var(--text-muted);
|
||
|
|
font-family: var(--font-mono);
|
||
|
|
}
|
||
|
|
|
||
|
|
.chart-body {
|
||
|
|
padding: 16px;
|
||
|
|
height: 220px;
|
||
|
|
position: relative;
|
||
|
|
}
|
||
|
|
|
||
|
|
.chart-body svg { width: 100%; height: 100%; }
|
||
|
|
|
||
|
|
/* Chart elements */
|
||
|
|
.chart-grid-line { stroke: var(--border-subtle); stroke-width: 1; stroke-dasharray: 3 3; }
|
||
|
|
.chart-axis-line { stroke: var(--border); stroke-width: 1; }
|
||
|
|
.chart-label { font-family: var(--font-mono); font-size: 9px; fill: var(--text-faint); }
|
||
|
|
.chart-value-label { font-family: var(--font-mono); font-size: 8px; fill: var(--text-muted); }
|
||
|
|
|
||
|
|
.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; }
|
||
|
|
|
||
|
|
.sla-zone { fill: var(--error); opacity: 0.04; }
|
||
|
|
|
||
|
|
/* Chart legend */
|
||
|
|
.chart-legend {
|
||
|
|
display: flex;
|
||
|
|
flex-wrap: wrap;
|
||
|
|
gap: 10px;
|
||
|
|
padding: 8px 16px 12px;
|
||
|
|
border-top: 1px solid var(--border-subtle);
|
||
|
|
}
|
||
|
|
|
||
|
|
.legend-item {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 5px;
|
||
|
|
font-size: 10px;
|
||
|
|
color: var(--text-muted);
|
||
|
|
font-family: var(--font-mono);
|
||
|
|
}
|
||
|
|
|
||
|
|
.legend-dot {
|
||
|
|
width: 8px;
|
||
|
|
height: 3px;
|
||
|
|
border-radius: 1px;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Heatmap */
|
||
|
|
.heatmap-grid {
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
gap: 2px;
|
||
|
|
height: 100%;
|
||
|
|
}
|
||
|
|
|
||
|
|
.heatmap-row {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 2px;
|
||
|
|
flex: 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
.heatmap-label {
|
||
|
|
width: 90px;
|
||
|
|
font-family: var(--font-mono);
|
||
|
|
font-size: 9px;
|
||
|
|
color: var(--text-muted);
|
||
|
|
text-align: right;
|
||
|
|
padding-right: 6px;
|
||
|
|
white-space: nowrap;
|
||
|
|
overflow: hidden;
|
||
|
|
text-overflow: ellipsis;
|
||
|
|
}
|
||
|
|
|
||
|
|
.heatmap-cells {
|
||
|
|
display: flex;
|
||
|
|
gap: 2px;
|
||
|
|
flex: 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
.heatmap-cell {
|
||
|
|
flex: 1;
|
||
|
|
border-radius: 2px;
|
||
|
|
position: relative;
|
||
|
|
cursor: pointer;
|
||
|
|
transition: transform 0.1s;
|
||
|
|
min-height: 16px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.heatmap-cell:hover { transform: scale(1.15); z-index: 1; }
|
||
|
|
|
||
|
|
.heatmap-hour-labels {
|
||
|
|
display: flex;
|
||
|
|
gap: 2px;
|
||
|
|
margin-left: 92px;
|
||
|
|
margin-top: 2px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.heatmap-hour-label {
|
||
|
|
flex: 1;
|
||
|
|
text-align: center;
|
||
|
|
font-family: var(--font-mono);
|
||
|
|
font-size: 8px;
|
||
|
|
color: var(--text-faint);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Heatmap intensity scale */
|
||
|
|
.heat-0 { background: var(--bg-inset); }
|
||
|
|
.heat-1 { background: #FDF6E9; }
|
||
|
|
.heat-2 { background: #F8E6C1; }
|
||
|
|
.heat-3 { background: #F0D9A8; }
|
||
|
|
.heat-4 { background: #E5C07A; }
|
||
|
|
.heat-5 { background: #D4A24E; }
|
||
|
|
.heat-6 { background: #C6820E; }
|
||
|
|
.heat-7 { background: #A66A0A; }
|
||
|
|
.heat-8 { background: #8B5A06; }
|
||
|
|
|
||
|
|
.heatmap-scale {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 4px;
|
||
|
|
padding: 6px 16px 10px;
|
||
|
|
justify-content: flex-end;
|
||
|
|
}
|
||
|
|
|
||
|
|
.heatmap-scale-label {
|
||
|
|
font-size: 9px;
|
||
|
|
color: var(--text-faint);
|
||
|
|
font-family: var(--font-mono);
|
||
|
|
}
|
||
|
|
|
||
|
|
.heatmap-scale-cells {
|
||
|
|
display: flex;
|
||
|
|
gap: 2px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.heatmap-scale-cell {
|
||
|
|
width: 12px;
|
||
|
|
height: 10px;
|
||
|
|
border-radius: 1px;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ==========================================================================
|
||
|
|
LIVE EVENT FEED
|
||
|
|
========================================================================== */
|
||
|
|
.live-feed-section {
|
||
|
|
background: var(--bg-surface);
|
||
|
|
border: 1px solid var(--border-subtle);
|
||
|
|
border-radius: var(--radius-lg);
|
||
|
|
box-shadow: var(--shadow-card);
|
||
|
|
overflow: hidden;
|
||
|
|
}
|
||
|
|
|
||
|
|
.live-feed-header {
|
||
|
|
padding: 10px 16px;
|
||
|
|
border-bottom: 1px solid var(--border-subtle);
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: space-between;
|
||
|
|
}
|
||
|
|
|
||
|
|
.live-feed-title {
|
||
|
|
font-size: 13px;
|
||
|
|
font-weight: 600;
|
||
|
|
color: var(--text-primary);
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 8px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.live-feed-dot {
|
||
|
|
width: 6px;
|
||
|
|
height: 6px;
|
||
|
|
border-radius: 50%;
|
||
|
|
background: var(--success);
|
||
|
|
animation: pulse 2s ease-in-out infinite;
|
||
|
|
}
|
||
|
|
|
||
|
|
.live-feed-list {
|
||
|
|
max-height: 240px;
|
||
|
|
overflow-y: auto;
|
||
|
|
}
|
||
|
|
|
||
|
|
.feed-item {
|
||
|
|
display: flex;
|
||
|
|
gap: 10px;
|
||
|
|
padding: 8px 16px;
|
||
|
|
border-bottom: 1px solid var(--border-subtle);
|
||
|
|
transition: background 0.08s;
|
||
|
|
cursor: pointer;
|
||
|
|
}
|
||
|
|
|
||
|
|
.feed-item:hover { background: var(--bg-hover); }
|
||
|
|
.feed-item:last-child { border-bottom: none; }
|
||
|
|
|
||
|
|
.feed-icon {
|
||
|
|
width: 24px;
|
||
|
|
height: 24px;
|
||
|
|
border-radius: 50%;
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
font-size: 11px;
|
||
|
|
flex-shrink: 0;
|
||
|
|
margin-top: 1px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.feed-icon.feed-error { background: var(--error-bg); color: var(--error); border: 1px solid var(--error-border); }
|
||
|
|
.feed-icon.feed-warn { background: var(--warning-bg); color: var(--warning); border: 1px solid var(--warning-border); }
|
||
|
|
.feed-icon.feed-info { background: var(--running-bg); color: var(--running); border: 1px solid var(--running-border); }
|
||
|
|
.feed-icon.feed-ok { background: var(--success-bg); color: var(--success); border: 1px solid var(--success-border); }
|
||
|
|
|
||
|
|
.feed-content { flex: 1; min-width: 0; }
|
||
|
|
|
||
|
|
.feed-text {
|
||
|
|
font-size: 12px;
|
||
|
|
color: var(--text-primary);
|
||
|
|
line-height: 1.4;
|
||
|
|
}
|
||
|
|
|
||
|
|
.feed-text strong { font-weight: 600; }
|
||
|
|
.feed-text .feed-route { color: var(--amber); font-family: var(--font-mono); font-size: 11px; }
|
||
|
|
|
||
|
|
.feed-meta {
|
||
|
|
font-size: 10px;
|
||
|
|
color: var(--text-faint);
|
||
|
|
font-family: var(--font-mono);
|
||
|
|
margin-top: 1px;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Utility */
|
||
|
|
.mono { font-family: var(--font-mono); }
|
||
|
|
</style>
|
||
|
|
</head>
|
||
|
|
<body>
|
||
|
|
<div class="app">
|
||
|
|
|
||
|
|
<!-- ====================================================================
|
||
|
|
SIDEBAR (warm charcoal — identical to v2, "Metrics" highlighted)
|
||
|
|
==================================================================== -->
|
||
|
|
<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.283
|
||
|
|
</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">Navigation</div>
|
||
|
|
|
||
|
|
<div class="sidebar-items">
|
||
|
|
<div class="sidebar-item">
|
||
|
|
<span style="font-size: 13px; width: 18px; text-align: center;">▢</span>
|
||
|
|
<div class="item-info"><div class="item-name">Transactions</div></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="sidebar-item active">
|
||
|
|
<span style="font-size: 13px; width: 18px; text-align: center;">◆</span>
|
||
|
|
<div class="item-info"><div class="item-name">Metrics</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">Route Diagrams</div></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="sidebar-divider"></div>
|
||
|
|
<div class="sidebar-section">Applications</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-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: 180px;">
|
||
|
|
<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">payment-svc 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 class="agent-item">
|
||
|
|
<span class="agent-dot health-live"></span>
|
||
|
|
<div class="agent-info">
|
||
|
|
<div class="agent-name">prod-3</div>
|
||
|
|
<div class="agent-detail">shipment-svc v3.2.0</div>
|
||
|
|
</div>
|
||
|
|
<div class="agent-stats-col">
|
||
|
|
<div class="agent-tps">12.1/s</div>
|
||
|
|
<div>5s ago</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="agent-item">
|
||
|
|
<span class="agent-dot health-live"></span>
|
||
|
|
<div class="agent-info">
|
||
|
|
<div class="agent-name">prod-4</div>
|
||
|
|
<div class="agent-detail">notif-hub v3.1.8</div>
|
||
|
|
</div>
|
||
|
|
<div class="agent-stats-col">
|
||
|
|
<div class="agent-tps">9.1/s</div>
|
||
|
|
<div>3s ago</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>Dashboard</span>
|
||
|
|
<span class="crumb-sep">/</span>
|
||
|
|
<span class="crumb-active">Metrics</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 area -->
|
||
|
|
<div class="content">
|
||
|
|
|
||
|
|
<!-- Time range selector bar -->
|
||
|
|
<div class="time-range-bar animate-in">
|
||
|
|
<div class="time-range-pills">
|
||
|
|
<button class="time-pill">1h</button>
|
||
|
|
<button class="time-pill active">6h</button>
|
||
|
|
<button class="time-pill">24h</button>
|
||
|
|
<button class="time-pill">7d</button>
|
||
|
|
<button class="time-pill">30d</button>
|
||
|
|
<button class="time-pill">Custom</button>
|
||
|
|
</div>
|
||
|
|
<div class="refresh-group">
|
||
|
|
<div class="auto-refresh">
|
||
|
|
<span class="auto-refresh-dot"></span>
|
||
|
|
Auto-refresh: 30s
|
||
|
|
</div>
|
||
|
|
<button class="refresh-btn">
|
||
|
|
<span class="refresh-icon">↻</span>
|
||
|
|
Refresh
|
||
|
|
</button>
|
||
|
|
<span class="last-updated">Updated 4s ago</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- ================================================================
|
||
|
|
KPI CARDS — System Health Overview
|
||
|
|
================================================================ -->
|
||
|
|
<div class="kpi-strip animate-in delay-1">
|
||
|
|
|
||
|
|
<!-- Card 1: Total Throughput -->
|
||
|
|
<div class="kpi-card card-amber">
|
||
|
|
<div class="kpi-label">Total Throughput</div>
|
||
|
|
<div class="kpi-value-row">
|
||
|
|
<span class="kpi-value val-amber">2,847</span>
|
||
|
|
<span class="kpi-unit">msg/min</span>
|
||
|
|
<span class="kpi-trend trend-up-good">▲ +8%</span>
|
||
|
|
</div>
|
||
|
|
<div class="kpi-detail"><strong>47.5</strong> msg/s · Capacity 39%</div>
|
||
|
|
<div class="kpi-sparkline">
|
||
|
|
<svg viewBox="0 0 200 32" preserveAspectRatio="none">
|
||
|
|
<polyline points="0,28 8,26 16,24 24,25 32,22 40,20 48,21 56,18 64,16 72,17 80,15 88,13 96,14 104,12 112,10 120,11 128,9 136,8 144,10 152,7 160,6 168,8 176,5 184,4 192,6 200,3" fill="none" stroke="#C6820E" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||
|
|
<linearGradient id="sparkGrad1" x1="0" y1="0" x2="0" y2="1">
|
||
|
|
<stop offset="0%" stop-color="#C6820E" stop-opacity="0.2"/>
|
||
|
|
<stop offset="100%" stop-color="#C6820E" stop-opacity="0"/>
|
||
|
|
</linearGradient>
|
||
|
|
<polyline points="0,28 8,26 16,24 24,25 32,22 40,20 48,21 56,18 64,16 72,17 80,15 88,13 96,14 104,12 112,10 120,11 128,9 136,8 144,10 152,7 160,6 168,8 176,5 184,4 192,6 200,3 200,32 0,32" fill="url(#sparkGrad1)"/>
|
||
|
|
</svg>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Card 2: System Error Rate -->
|
||
|
|
<div class="kpi-card card-green">
|
||
|
|
<div class="kpi-label">System Error Rate</div>
|
||
|
|
<div class="kpi-value-row">
|
||
|
|
<span class="kpi-value val-green">0.82%</span>
|
||
|
|
<span class="kpi-trend trend-down-good">▼ -0.1%</span>
|
||
|
|
</div>
|
||
|
|
<div class="kpi-detail"><strong>94</strong> errors / <strong>11,482</strong> total (6h)</div>
|
||
|
|
<div class="kpi-sparkline">
|
||
|
|
<svg viewBox="0 0 200 32" preserveAspectRatio="none">
|
||
|
|
<polyline points="0,18 8,17 16,19 24,16 32,15 40,17 48,14 56,16 64,13 72,15 80,12 88,14 96,11 104,13 112,10 120,12 128,14 136,11 144,13 152,10 160,12 168,10 176,11 184,9 192,10 200,8" fill="none" stroke="#3D7C47" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||
|
|
<linearGradient id="sparkGrad2" x1="0" y1="0" x2="0" y2="1">
|
||
|
|
<stop offset="0%" stop-color="#3D7C47" stop-opacity="0.15"/>
|
||
|
|
<stop offset="100%" stop-color="#3D7C47" stop-opacity="0"/>
|
||
|
|
</linearGradient>
|
||
|
|
<polyline points="0,18 8,17 16,19 24,16 32,15 40,17 48,14 56,16 64,13 72,15 80,12 88,14 96,11 104,13 112,10 120,12 128,14 136,11 144,13 152,10 160,12 168,10 176,11 184,9 192,10 200,8 200,32 0,32" fill="url(#sparkGrad2)"/>
|
||
|
|
</svg>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Card 3: Avg Latency (P50/P95/P99) -->
|
||
|
|
<div class="kpi-card card-warn">
|
||
|
|
<div class="kpi-label">Latency Percentiles</div>
|
||
|
|
<div class="latency-values">
|
||
|
|
<div class="latency-item">
|
||
|
|
<span class="latency-label">P50</span>
|
||
|
|
<span class="latency-val lat-green">42ms</span>
|
||
|
|
<span class="latency-trend trend-down-good">▼3</span>
|
||
|
|
</div>
|
||
|
|
<div class="latency-item">
|
||
|
|
<span class="latency-label">P95</span>
|
||
|
|
<span class="latency-val lat-amber">187ms</span>
|
||
|
|
<span class="latency-trend trend-up-bad">▲12</span>
|
||
|
|
</div>
|
||
|
|
<div class="latency-item">
|
||
|
|
<span class="latency-label">P99</span>
|
||
|
|
<span class="latency-val lat-red">312ms</span>
|
||
|
|
<span class="latency-trend trend-up-bad">▲28</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="kpi-detail" style="margin-top:6px;">SLA: <300ms P99 · <strong style="color:var(--error);">BREACH</strong></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Card 4: Active Routes (mini donut) -->
|
||
|
|
<div class="kpi-card card-teal">
|
||
|
|
<div class="kpi-label">Active Routes</div>
|
||
|
|
<div class="kpi-value-row">
|
||
|
|
<span class="kpi-value val-teal">7</span>
|
||
|
|
<span class="kpi-unit">of 8</span>
|
||
|
|
<span class="kpi-trend trend-flat">↔ stable</span>
|
||
|
|
</div>
|
||
|
|
<div class="mini-donut-wrap">
|
||
|
|
<div class="mini-donut">
|
||
|
|
<svg viewBox="0 0 36 36" width="40" height="40">
|
||
|
|
<circle cx="18" cy="18" r="15.9" fill="none" stroke="var(--bg-inset)" stroke-width="3"/>
|
||
|
|
<circle cx="18" cy="18" r="15.9" fill="none" stroke="var(--running)" stroke-width="3"
|
||
|
|
stroke-dasharray="87.5 12.5" stroke-dashoffset="25" stroke-linecap="round"/>
|
||
|
|
</svg>
|
||
|
|
<span class="donut-label">88%</span>
|
||
|
|
</div>
|
||
|
|
<div class="donut-legend">
|
||
|
|
<span class="donut-legend-active">7 active</span>
|
||
|
|
<span>1 stopped</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Card 5: In-Flight Exchanges -->
|
||
|
|
<div class="kpi-card card-amber">
|
||
|
|
<div class="kpi-label">In-Flight Exchanges</div>
|
||
|
|
<div class="kpi-value-row">
|
||
|
|
<span class="kpi-value">23</span>
|
||
|
|
<span class="kpi-trend trend-flat">↔</span>
|
||
|
|
</div>
|
||
|
|
<div class="kpi-detail">High-water: <strong>67</strong> (2h ago)</div>
|
||
|
|
<div class="kpi-sparkline">
|
||
|
|
<svg viewBox="0 0 200 32" preserveAspectRatio="none">
|
||
|
|
<polyline points="0,16 8,14 16,18 24,12 32,10 40,15 48,8 56,6 64,4 72,3 80,2 88,4 96,6 104,8 112,10 120,12 128,14 136,16 144,18 152,20 160,18 168,16 176,18 184,20 192,18 200,17" fill="none" stroke="#C6820E" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||
|
|
<linearGradient id="sparkGrad5" x1="0" y1="0" x2="0" y2="1">
|
||
|
|
<stop offset="0%" stop-color="#C6820E" stop-opacity="0.15"/>
|
||
|
|
<stop offset="100%" stop-color="#C6820E" stop-opacity="0"/>
|
||
|
|
</linearGradient>
|
||
|
|
<polyline points="0,16 8,14 16,18 24,12 32,10 40,15 48,8 56,6 64,4 72,3 80,2 88,4 96,6 104,8 112,10 120,12 128,14 136,16 144,18 152,20 160,18 168,16 176,18 184,20 192,18 200,17 200,32 0,32" fill="url(#sparkGrad5)"/>
|
||
|
|
<!-- High-water mark line -->
|
||
|
|
<line x1="78" y1="0" x2="78" y2="32" stroke="var(--error)" stroke-width="0.5" stroke-dasharray="2 2" opacity="0.5"/>
|
||
|
|
<text x="80" y="7" font-family="var(--font-mono)" font-size="6" fill="var(--error)" opacity="0.6">67</text>
|
||
|
|
</svg>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- ================================================================
|
||
|
|
PER-ROUTE PERFORMANCE TABLE
|
||
|
|
================================================================ -->
|
||
|
|
<div class="table-section animate-in delay-2">
|
||
|
|
<div class="table-header">
|
||
|
|
<span class="table-title">Per-Route Performance</span>
|
||
|
|
<div class="table-right">
|
||
|
|
<span class="table-meta">8 routes · last 6h</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="table-scroll">
|
||
|
|
<table>
|
||
|
|
<thead>
|
||
|
|
<tr>
|
||
|
|
<th style="width:180px">Route</th>
|
||
|
|
<th>Trend (1h)</th>
|
||
|
|
<th class="sorted">Throughput <span class="sort-arrow">▼</span></th>
|
||
|
|
<th>Error Rate</th>
|
||
|
|
<th>P50</th>
|
||
|
|
<th>P95</th>
|
||
|
|
<th>P99</th>
|
||
|
|
<th style="text-align:right">Success</th>
|
||
|
|
<th style="text-align:right">Failed</th>
|
||
|
|
<th style="width:80px">Status</th>
|
||
|
|
<th style="width:30px"></th>
|
||
|
|
</tr>
|
||
|
|
</thead>
|
||
|
|
<tbody>
|
||
|
|
<!-- Row 1: order-processing — high throughput, healthy -->
|
||
|
|
<tr>
|
||
|
|
<td>
|
||
|
|
<div class="route-name-cell">order-processing</div>
|
||
|
|
<div class="route-group-cell">order-service</div>
|
||
|
|
</td>
|
||
|
|
<td>
|
||
|
|
<span class="cell-sparkline">
|
||
|
|
<svg width="80" height="20" viewBox="0 0 80 20">
|
||
|
|
<polyline points="0,14 8,12 16,10 24,11 32,8 40,9 48,7 56,6 64,7 72,5 80,4" fill="none" stroke="var(--chart-1)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||
|
|
</svg>
|
||
|
|
</span>
|
||
|
|
</td>
|
||
|
|
<td><span class="mono" style="font-size:12px;font-weight:600;">842</span> <span style="font-size:10px;color:var(--text-muted);">msg/min</span></td>
|
||
|
|
<td><span class="err-rate err-low">0.2%</span></td>
|
||
|
|
<td><span class="lat-cell lat-fast">28ms</span></td>
|
||
|
|
<td><span class="lat-cell lat-fast">94ms</span></td>
|
||
|
|
<td><span class="lat-cell lat-normal">156ms</span></td>
|
||
|
|
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--success);">4,987</span></td>
|
||
|
|
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--error);">10</span></td>
|
||
|
|
<td><span class="status-badge status-success"><span class="status-dot"></span>OK</span></td>
|
||
|
|
<td><span class="drill-arrow">▸</span></td>
|
||
|
|
</tr>
|
||
|
|
|
||
|
|
<!-- Row 2: payment-flow — higher latency, some errors -->
|
||
|
|
<tr>
|
||
|
|
<td>
|
||
|
|
<div class="route-name-cell">payment-flow</div>
|
||
|
|
<div class="route-group-cell">payment-gateway</div>
|
||
|
|
</td>
|
||
|
|
<td>
|
||
|
|
<span class="cell-sparkline">
|
||
|
|
<svg width="80" height="20" viewBox="0 0 80 20">
|
||
|
|
<polyline points="0,10 8,11 16,9 24,12 32,10 40,8 48,10 56,9 64,11 72,8 80,7" fill="none" stroke="var(--chart-2)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||
|
|
</svg>
|
||
|
|
</span>
|
||
|
|
</td>
|
||
|
|
<td><span class="mono" style="font-size:12px;font-weight:600;">524</span> <span style="font-size:10px;color:var(--text-muted);">msg/min</span></td>
|
||
|
|
<td><span class="err-rate err-mid">2.1%</span></td>
|
||
|
|
<td><span class="lat-cell lat-normal">89ms</span></td>
|
||
|
|
<td><span class="lat-cell lat-slow">245ms</span></td>
|
||
|
|
<td><span class="lat-cell lat-breach">412ms</span></td>
|
||
|
|
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--success);">3,078</span></td>
|
||
|
|
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--error);">66</span></td>
|
||
|
|
<td><span class="status-badge status-warning"><span class="status-dot"></span>SLA</span></td>
|
||
|
|
<td><span class="drill-arrow">▸</span></td>
|
||
|
|
</tr>
|
||
|
|
|
||
|
|
<!-- Row 3: shipment-notify — steady, fast -->
|
||
|
|
<tr>
|
||
|
|
<td>
|
||
|
|
<div class="route-name-cell">shipment-notify</div>
|
||
|
|
<div class="route-group-cell">shipment-tracker</div>
|
||
|
|
</td>
|
||
|
|
<td>
|
||
|
|
<span class="cell-sparkline">
|
||
|
|
<svg width="80" height="20" viewBox="0 0 80 20">
|
||
|
|
<polyline points="0,10 8,10 16,9 24,10 32,10 40,9 48,10 56,9 64,10 72,9 80,10" fill="none" stroke="var(--chart-3)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||
|
|
</svg>
|
||
|
|
</span>
|
||
|
|
</td>
|
||
|
|
<td><span class="mono" style="font-size:12px;font-weight:600;">471</span> <span style="font-size:10px;color:var(--text-muted);">msg/min</span></td>
|
||
|
|
<td><span class="err-rate err-low">0.1%</span></td>
|
||
|
|
<td><span class="lat-cell lat-fast">18ms</span></td>
|
||
|
|
<td><span class="lat-cell lat-fast">52ms</span></td>
|
||
|
|
<td><span class="lat-cell lat-fast">87ms</span></td>
|
||
|
|
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--success);">2,823</span></td>
|
||
|
|
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--error);">3</span></td>
|
||
|
|
<td><span class="status-badge status-success"><span class="status-dot"></span>OK</span></td>
|
||
|
|
<td><span class="drill-arrow">▸</span></td>
|
||
|
|
</tr>
|
||
|
|
|
||
|
|
<!-- Row 4: inventory-check — fast, low volume -->
|
||
|
|
<tr>
|
||
|
|
<td>
|
||
|
|
<div class="route-name-cell">inventory-check</div>
|
||
|
|
<div class="route-group-cell">order-service</div>
|
||
|
|
</td>
|
||
|
|
<td>
|
||
|
|
<span class="cell-sparkline">
|
||
|
|
<svg width="80" height="20" viewBox="0 0 80 20">
|
||
|
|
<polyline points="0,12 8,13 16,11 24,14 32,12 40,13 48,11 56,12 64,13 72,11 80,12" fill="none" stroke="var(--chart-4)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||
|
|
</svg>
|
||
|
|
</span>
|
||
|
|
</td>
|
||
|
|
<td><span class="mono" style="font-size:12px;font-weight:600;">389</span> <span style="font-size:10px;color:var(--text-muted);">msg/min</span></td>
|
||
|
|
<td><span class="err-rate err-low">0.3%</span></td>
|
||
|
|
<td><span class="lat-cell lat-fast">12ms</span></td>
|
||
|
|
<td><span class="lat-cell lat-fast">34ms</span></td>
|
||
|
|
<td><span class="lat-cell lat-fast">61ms</span></td>
|
||
|
|
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--success);">2,327</span></td>
|
||
|
|
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--error);">7</span></td>
|
||
|
|
<td><span class="status-badge status-success"><span class="status-dot"></span>OK</span></td>
|
||
|
|
<td><span class="drill-arrow">▸</span></td>
|
||
|
|
</tr>
|
||
|
|
|
||
|
|
<!-- Row 5: customer-validation — moderate, stable -->
|
||
|
|
<tr>
|
||
|
|
<td>
|
||
|
|
<div class="route-name-cell">customer-validation</div>
|
||
|
|
<div class="route-group-cell">order-service</div>
|
||
|
|
</td>
|
||
|
|
<td>
|
||
|
|
<span class="cell-sparkline">
|
||
|
|
<svg width="80" height="20" viewBox="0 0 80 20">
|
||
|
|
<polyline points="0,8 8,10 16,9 24,8 32,10 40,9 48,10 56,8 64,9 72,10 80,9" fill="none" stroke="var(--chart-5)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||
|
|
</svg>
|
||
|
|
</span>
|
||
|
|
</td>
|
||
|
|
<td><span class="mono" style="font-size:12px;font-weight:600;">312</span> <span style="font-size:10px;color:var(--text-muted);">msg/min</span></td>
|
||
|
|
<td><span class="err-rate err-low">0.5%</span></td>
|
||
|
|
<td><span class="lat-cell lat-fast">35ms</span></td>
|
||
|
|
<td><span class="lat-cell lat-normal">112ms</span></td>
|
||
|
|
<td><span class="lat-cell lat-normal">178ms</span></td>
|
||
|
|
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--success);">1,863</span></td>
|
||
|
|
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--error);">9</span></td>
|
||
|
|
<td><span class="status-badge status-success"><span class="status-dot"></span>OK</span></td>
|
||
|
|
<td><span class="drill-arrow">▸</span></td>
|
||
|
|
</tr>
|
||
|
|
|
||
|
|
<!-- Row 6: email-notification — low volume, very fast -->
|
||
|
|
<tr>
|
||
|
|
<td>
|
||
|
|
<div class="route-name-cell">email-notification</div>
|
||
|
|
<div class="route-group-cell">notification-hub</div>
|
||
|
|
</td>
|
||
|
|
<td>
|
||
|
|
<span class="cell-sparkline">
|
||
|
|
<svg width="80" height="20" viewBox="0 0 80 20">
|
||
|
|
<polyline points="0,14 8,15 16,13 24,14 32,16 40,14 48,15 56,13 64,14 72,15 80,14" fill="none" stroke="var(--chart-6)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||
|
|
</svg>
|
||
|
|
</span>
|
||
|
|
</td>
|
||
|
|
<td><span class="mono" style="font-size:12px;font-weight:600;">201</span> <span style="font-size:10px;color:var(--text-muted);">msg/min</span></td>
|
||
|
|
<td><span class="err-rate err-low">0.0%</span></td>
|
||
|
|
<td><span class="lat-cell lat-fast">8ms</span></td>
|
||
|
|
<td><span class="lat-cell lat-fast">21ms</span></td>
|
||
|
|
<td><span class="lat-cell lat-fast">38ms</span></td>
|
||
|
|
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--success);">1,206</span></td>
|
||
|
|
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--text-faint);">0</span></td>
|
||
|
|
<td><span class="status-badge status-success"><span class="status-dot"></span>OK</span></td>
|
||
|
|
<td><span class="drill-arrow">▸</span></td>
|
||
|
|
</tr>
|
||
|
|
|
||
|
|
<!-- Row 7: retry-handler — low throughput, HIGH error rate -->
|
||
|
|
<tr>
|
||
|
|
<td>
|
||
|
|
<div class="route-name-cell">retry-handler</div>
|
||
|
|
<div class="route-group-cell">order-service</div>
|
||
|
|
</td>
|
||
|
|
<td>
|
||
|
|
<span class="cell-sparkline">
|
||
|
|
<svg width="80" height="20" viewBox="0 0 80 20">
|
||
|
|
<polyline points="0,10 8,8 16,12 24,6 32,10 40,4 48,8 56,6 64,10 72,4 80,8" fill="none" stroke="var(--chart-7)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||
|
|
</svg>
|
||
|
|
</span>
|
||
|
|
</td>
|
||
|
|
<td><span class="mono" style="font-size:12px;font-weight:600;">78</span> <span style="font-size:10px;color:var(--text-muted);">msg/min</span></td>
|
||
|
|
<td><span class="err-rate err-high">12.8%</span></td>
|
||
|
|
<td><span class="lat-cell lat-slow">210ms</span></td>
|
||
|
|
<td><span class="lat-cell lat-breach">890ms</span></td>
|
||
|
|
<td><span class="lat-cell lat-breach">2,140ms</span></td>
|
||
|
|
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--success);">408</span></td>
|
||
|
|
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--error);">60</span></td>
|
||
|
|
<td><span class="status-badge status-warning"><span class="status-dot"></span>WARN</span></td>
|
||
|
|
<td><span class="drill-arrow">▸</span></td>
|
||
|
|
</tr>
|
||
|
|
|
||
|
|
<!-- Row 8: dead-letter-processor — very low, all "errors" -->
|
||
|
|
<tr>
|
||
|
|
<td>
|
||
|
|
<div class="route-name-cell" style="color:var(--text-muted);">dead-letter-processor</div>
|
||
|
|
<div class="route-group-cell">order-service</div>
|
||
|
|
</td>
|
||
|
|
<td>
|
||
|
|
<span class="cell-sparkline">
|
||
|
|
<svg width="80" height="20" viewBox="0 0 80 20">
|
||
|
|
<polyline points="0,16 8,16 16,14 24,16 32,15 40,16 48,14 56,16 64,15 72,16 80,16" fill="none" stroke="var(--text-faint)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="3 2"/>
|
||
|
|
</svg>
|
||
|
|
</span>
|
||
|
|
</td>
|
||
|
|
<td><span class="mono" style="font-size:12px;color:var(--text-muted);">0</span> <span style="font-size:10px;color:var(--text-faint);">msg/min</span></td>
|
||
|
|
<td><span class="err-rate" style="color:var(--text-faint);">-- %</span></td>
|
||
|
|
<td><span class="lat-cell" style="color:var(--text-faint);">--</span></td>
|
||
|
|
<td><span class="lat-cell" style="color:var(--text-faint);">--</span></td>
|
||
|
|
<td><span class="lat-cell" style="color:var(--text-faint);">--</span></td>
|
||
|
|
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--text-faint);">0</span></td>
|
||
|
|
<td style="text-align:right"><span class="mono" style="font-size:12px;color:var(--text-faint);">0</span></td>
|
||
|
|
<td><span class="status-badge" style="background:var(--bg-inset);color:var(--text-faint);border:1px solid var(--border);"><span class="status-dot"></span>STOP</span></td>
|
||
|
|
<td><span class="drill-arrow">▸</span></td>
|
||
|
|
</tr>
|
||
|
|
|
||
|
|
</tbody>
|
||
|
|
</table>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- ================================================================
|
||
|
|
TIME-SERIES CHARTS (2x2 grid)
|
||
|
|
================================================================ -->
|
||
|
|
<div class="chart-grid animate-in delay-3">
|
||
|
|
|
||
|
|
<!-- Chart 1: Throughput Over Time (stacked area) -->
|
||
|
|
<div class="chart-card">
|
||
|
|
<div class="chart-card-header">
|
||
|
|
<div>
|
||
|
|
<div class="chart-card-title">Throughput Over Time</div>
|
||
|
|
<div class="chart-card-subtitle">Messages/min by route · last 6h</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="chart-body">
|
||
|
|
<svg viewBox="0 0 600 180" preserveAspectRatio="none">
|
||
|
|
<!-- Grid lines -->
|
||
|
|
<line x1="40" y1="10" x2="580" y2="10" class="chart-grid-line"/>
|
||
|
|
<line x1="40" y1="52" x2="580" y2="52" class="chart-grid-line"/>
|
||
|
|
<line x1="40" y1="94" x2="580" y2="94" class="chart-grid-line"/>
|
||
|
|
<line x1="40" y1="136" x2="580" y2="136" class="chart-grid-line"/>
|
||
|
|
<line x1="40" y1="170" x2="580" y2="170" class="chart-axis-line"/>
|
||
|
|
|
||
|
|
<!-- Y-axis labels -->
|
||
|
|
<text x="36" y="14" class="chart-label" text-anchor="end">3k</text>
|
||
|
|
<text x="36" y="56" class="chart-label" text-anchor="end">2k</text>
|
||
|
|
<text x="36" y="98" class="chart-label" text-anchor="end">1k</text>
|
||
|
|
<text x="36" y="140" class="chart-label" text-anchor="end">500</text>
|
||
|
|
<text x="36" y="174" class="chart-label" text-anchor="end">0</text>
|
||
|
|
|
||
|
|
<!-- X-axis labels -->
|
||
|
|
<text x="40" y="180" class="chart-label">03:00</text>
|
||
|
|
<text x="130" y="180" class="chart-label">04:00</text>
|
||
|
|
<text x="220" y="180" class="chart-label">05:00</text>
|
||
|
|
<text x="310" y="180" class="chart-label">06:00</text>
|
||
|
|
<text x="400" y="180" class="chart-label">07:00</text>
|
||
|
|
<text x="490" y="180" class="chart-label">08:00</text>
|
||
|
|
<text x="565" y="180" class="chart-label">09:14</text>
|
||
|
|
|
||
|
|
<!-- Stacked areas (bottom to top: email, inventory, shipment, customer, payment, order) -->
|
||
|
|
<!-- Layer 1: order-processing (amber) -->
|
||
|
|
<polygon points="40,170 85,165 130,160 175,155 220,145 265,130 310,105 355,90 400,75 445,65 490,55 535,50 580,45 580,170" fill="#C6820E" opacity="0.3"/>
|
||
|
|
<polyline points="40,170 85,165 130,160 175,155 220,145 265,130 310,105 355,90 400,75 445,65 490,55 535,50 580,45" fill="none" stroke="#C6820E" stroke-width="1.5"/>
|
||
|
|
|
||
|
|
<!-- Layer 2: payment-flow (olive green) -->
|
||
|
|
<polygon points="40,170 85,168 130,166 175,163 220,158 265,150 310,135 355,125 400,112 445,104 490,96 535,92 580,88 580,170" fill="#3D7C47" opacity="0.25"/>
|
||
|
|
<polyline points="40,170 85,168 130,166 175,163 220,158 265,150 310,135 355,125 400,112 445,104 490,96 535,92 580,88" fill="none" stroke="#3D7C47" stroke-width="1.5"/>
|
||
|
|
|
||
|
|
<!-- Layer 3: shipment-notify (teal) -->
|
||
|
|
<polygon points="40,170 85,169 130,168 175,167 220,164 265,158 310,148 355,140 400,132 445,126 490,120 535,117 580,114 580,170" fill="#1A7F8E" opacity="0.25"/>
|
||
|
|
<polyline points="40,170 85,169 130,168 175,167 220,164 265,158 310,148 355,140 400,132 445,126 490,120 535,117 580,114" fill="none" stroke="#1A7F8E" stroke-width="1.5"/>
|
||
|
|
|
||
|
|
<!-- Layer 4: other routes combined (burnt orange, thin) -->
|
||
|
|
<polygon points="40,170 85,169 130,169 175,168 220,167 265,164 310,158 355,152 400,146 445,141 490,136 535,134 580,132 580,170" fill="#C27516" opacity="0.15"/>
|
||
|
|
<polyline points="40,170 85,169 130,169 175,168 220,167 265,164 310,158 355,152 400,146 445,141 490,136 535,134 580,132" fill="none" stroke="#C27516" stroke-width="1" stroke-dasharray="3 2"/>
|
||
|
|
</svg>
|
||
|
|
</div>
|
||
|
|
<div class="chart-legend">
|
||
|
|
<div class="legend-item"><div class="legend-dot" style="background:var(--chart-1);"></div>order-processing</div>
|
||
|
|
<div class="legend-item"><div class="legend-dot" style="background:var(--chart-2);"></div>payment-flow</div>
|
||
|
|
<div class="legend-item"><div class="legend-dot" style="background:var(--chart-3);"></div>shipment-notify</div>
|
||
|
|
<div class="legend-item"><div class="legend-dot" style="background:var(--chart-4);"></div>others (combined)</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Chart 2: Latency Percentiles (line chart) -->
|
||
|
|
<div class="chart-card">
|
||
|
|
<div class="chart-card-header">
|
||
|
|
<div>
|
||
|
|
<div class="chart-card-title">Latency Percentiles</div>
|
||
|
|
<div class="chart-card-subtitle">P50 / P95 / P99 · last 6h</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="chart-body">
|
||
|
|
<svg viewBox="0 0 600 180" preserveAspectRatio="none">
|
||
|
|
<!-- Grid -->
|
||
|
|
<line x1="40" y1="10" x2="580" y2="10" class="chart-grid-line"/>
|
||
|
|
<line x1="40" y1="52" x2="580" y2="52" class="chart-grid-line"/>
|
||
|
|
<line x1="40" y1="94" x2="580" y2="94" class="chart-grid-line"/>
|
||
|
|
<line x1="40" y1="136" x2="580" y2="136" class="chart-grid-line"/>
|
||
|
|
<line x1="40" y1="170" x2="580" y2="170" class="chart-axis-line"/>
|
||
|
|
|
||
|
|
<!-- Y-axis (ms) -->
|
||
|
|
<text x="36" y="14" class="chart-label" text-anchor="end">500</text>
|
||
|
|
<text x="36" y="56" class="chart-label" text-anchor="end">375</text>
|
||
|
|
<text x="36" y="98" class="chart-label" text-anchor="end">250</text>
|
||
|
|
<text x="36" y="140" class="chart-label" text-anchor="end">125</text>
|
||
|
|
<text x="36" y="174" class="chart-label" text-anchor="end">0</text>
|
||
|
|
|
||
|
|
<!-- X-axis -->
|
||
|
|
<text x="40" y="180" class="chart-label">03:00</text>
|
||
|
|
<text x="130" y="180" class="chart-label">04:00</text>
|
||
|
|
<text x="220" y="180" class="chart-label">05:00</text>
|
||
|
|
<text x="310" y="180" class="chart-label">06:00</text>
|
||
|
|
<text x="400" y="180" class="chart-label">07:00</text>
|
||
|
|
<text x="490" y="180" class="chart-label">08:00</text>
|
||
|
|
<text x="565" y="180" class="chart-label">09:14</text>
|
||
|
|
|
||
|
|
<!-- SLA threshold at 300ms = y ~74 -->
|
||
|
|
<rect x="40" y="10" width="540" height="64" class="sla-zone"/>
|
||
|
|
<line x1="40" y1="74" x2="580" y2="74" class="sla-line"/>
|
||
|
|
<text x="582" y="72" class="sla-label-text">SLA 300ms</text>
|
||
|
|
|
||
|
|
<!-- P99 line (red, most volatile) -->
|
||
|
|
<polyline points="40,100 85,95 130,88 175,82 220,78 265,72 310,68 355,62 400,58 445,55 490,52 535,48 580,42" fill="none" stroke="var(--error)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
|
|
|
||
|
|
<!-- P95 line (amber) -->
|
||
|
|
<polyline points="40,128 85,126 130,122 175,118 220,114 265,110 310,106 355,102 400,98 445,96 490,94 535,92 580,90" fill="none" stroke="var(--warning)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
|
|
|
||
|
|
<!-- P50 line (green, stable at bottom) -->
|
||
|
|
<polyline points="40,158 85,157 130,156 175,155 220,154 265,153 310,152 355,152 400,151 445,150 490,150 535,149 580,148" fill="none" stroke="var(--success)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
|
|
|
||
|
|
<!-- Current value labels -->
|
||
|
|
<text x="582" y="44" class="chart-value-label" fill="var(--error)">312ms</text>
|
||
|
|
<text x="582" y="92" class="chart-value-label" fill="var(--warning)">187ms</text>
|
||
|
|
<text x="582" y="150" class="chart-value-label" fill="var(--success)">42ms</text>
|
||
|
|
</svg>
|
||
|
|
</div>
|
||
|
|
<div class="chart-legend">
|
||
|
|
<div class="legend-item"><div class="legend-dot" style="background:var(--success);"></div>P50</div>
|
||
|
|
<div class="legend-item"><div class="legend-dot" style="background:var(--warning);"></div>P95</div>
|
||
|
|
<div class="legend-item"><div class="legend-dot" style="background:var(--error);"></div>P99</div>
|
||
|
|
<div class="legend-item"><div class="legend-dot" style="background:var(--error); opacity:0.3;"></div>SLA threshold (300ms)</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Chart 3: Error Rate by Route (stacked bar chart) -->
|
||
|
|
<div class="chart-card">
|
||
|
|
<div class="chart-card-header">
|
||
|
|
<div>
|
||
|
|
<div class="chart-card-title">Errors by Route</div>
|
||
|
|
<div class="chart-card-subtitle">15-min buckets · last 6h</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="chart-body">
|
||
|
|
<svg viewBox="0 0 600 180" preserveAspectRatio="none">
|
||
|
|
<!-- Grid -->
|
||
|
|
<line x1="40" y1="10" x2="580" y2="10" class="chart-grid-line"/>
|
||
|
|
<line x1="40" y1="52" x2="580" y2="52" class="chart-grid-line"/>
|
||
|
|
<line x1="40" y1="94" x2="580" y2="94" class="chart-grid-line"/>
|
||
|
|
<line x1="40" y1="136" x2="580" y2="136" class="chart-grid-line"/>
|
||
|
|
<line x1="40" y1="170" x2="580" y2="170" class="chart-axis-line"/>
|
||
|
|
|
||
|
|
<!-- Y-axis -->
|
||
|
|
<text x="36" y="14" class="chart-label" text-anchor="end">20</text>
|
||
|
|
<text x="36" y="56" class="chart-label" text-anchor="end">15</text>
|
||
|
|
<text x="36" y="98" class="chart-label" text-anchor="end">10</text>
|
||
|
|
<text x="36" y="140" class="chart-label" text-anchor="end">5</text>
|
||
|
|
<text x="36" y="174" class="chart-label" text-anchor="end">0</text>
|
||
|
|
|
||
|
|
<!-- X-axis -->
|
||
|
|
<text x="40" y="180" class="chart-label">03:00</text>
|
||
|
|
<text x="130" y="180" class="chart-label">04:00</text>
|
||
|
|
<text x="220" y="180" class="chart-label">05:00</text>
|
||
|
|
<text x="310" y="180" class="chart-label">06:00</text>
|
||
|
|
<text x="400" y="180" class="chart-label">07:00</text>
|
||
|
|
<text x="490" y="180" class="chart-label">08:00</text>
|
||
|
|
<text x="565" y="180" class="chart-label">09:14</text>
|
||
|
|
|
||
|
|
<!-- Stacked bars (24 bars over 6 hours, 15-min each) -->
|
||
|
|
<!-- Low activity early morning (03:00-06:00) -->
|
||
|
|
<!-- Bar group 1: 03:00 -->
|
||
|
|
<rect x="44" y="162" width="16" height="8" rx="1" fill="var(--chart-7)" opacity="0.8"/>
|
||
|
|
<rect x="44" y="166" width="16" height="4" rx="1" fill="var(--chart-2)" opacity="0.6"/>
|
||
|
|
|
||
|
|
<!-- 03:15 -->
|
||
|
|
<rect x="66" y="166" width="16" height="4" rx="1" fill="var(--chart-7)" opacity="0.8"/>
|
||
|
|
|
||
|
|
<!-- 03:30 -->
|
||
|
|
<rect x="88" y="162" width="16" height="8" rx="1" fill="var(--chart-7)" opacity="0.8"/>
|
||
|
|
|
||
|
|
<!-- 03:45 -->
|
||
|
|
<rect x="110" y="164" width="16" height="6" rx="1" fill="var(--chart-7)" opacity="0.8"/>
|
||
|
|
|
||
|
|
<!-- 04:00 -->
|
||
|
|
<rect x="132" y="158" width="16" height="12" rx="1" fill="var(--chart-7)" opacity="0.8"/>
|
||
|
|
<rect x="132" y="162" width="16" height="8" rx="1" fill="var(--chart-1)" opacity="0.5"/>
|
||
|
|
|
||
|
|
<!-- 04:15 -->
|
||
|
|
<rect x="154" y="164" width="16" height="6" rx="1" fill="var(--chart-7)" opacity="0.8"/>
|
||
|
|
|
||
|
|
<!-- 04:30 -->
|
||
|
|
<rect x="176" y="160" width="16" height="10" rx="1" fill="var(--chart-7)" opacity="0.8"/>
|
||
|
|
|
||
|
|
<!-- 04:45 -->
|
||
|
|
<rect x="198" y="162" width="16" height="8" rx="1" fill="var(--chart-7)" opacity="0.8"/>
|
||
|
|
|
||
|
|
<!-- 05:00 -->
|
||
|
|
<rect x="220" y="158" width="16" height="12" rx="1" fill="var(--chart-7)" opacity="0.8"/>
|
||
|
|
<rect x="220" y="164" width="16" height="6" rx="1" fill="var(--chart-2)" opacity="0.6"/>
|
||
|
|
|
||
|
|
<!-- 05:15 -->
|
||
|
|
<rect x="242" y="164" width="16" height="6" rx="1" fill="var(--chart-7)" opacity="0.8"/>
|
||
|
|
|
||
|
|
<!-- 05:30 -->
|
||
|
|
<rect x="264" y="162" width="16" height="8" rx="1" fill="var(--chart-7)" opacity="0.8"/>
|
||
|
|
|
||
|
|
<!-- 05:45 -->
|
||
|
|
<rect x="286" y="160" width="16" height="10" rx="1" fill="var(--chart-7)" opacity="0.8"/>
|
||
|
|
|
||
|
|
<!-- 06:00 — shift start, traffic ramp up -->
|
||
|
|
<rect x="308" y="148" width="16" height="22" rx="1" fill="var(--chart-7)" opacity="0.8"/>
|
||
|
|
<rect x="308" y="154" width="16" height="16" rx="1" fill="var(--chart-2)" opacity="0.6"/>
|
||
|
|
<rect x="308" y="160" width="16" height="10" rx="1" fill="var(--chart-1)" opacity="0.5"/>
|
||
|
|
|
||
|
|
<!-- 06:15 -->
|
||
|
|
<rect x="330" y="152" width="16" height="18" rx="1" fill="var(--chart-7)" opacity="0.8"/>
|
||
|
|
<rect x="330" y="158" width="16" height="12" rx="1" fill="var(--chart-2)" opacity="0.6"/>
|
||
|
|
|
||
|
|
<!-- 06:30 -->
|
||
|
|
<rect x="352" y="144" width="16" height="26" rx="1" fill="var(--chart-7)" opacity="0.8"/>
|
||
|
|
<rect x="352" y="150" width="16" height="20" rx="1" fill="var(--chart-2)" opacity="0.6"/>
|
||
|
|
<rect x="352" y="158" width="16" height="12" rx="1" fill="var(--chart-1)" opacity="0.5"/>
|
||
|
|
|
||
|
|
<!-- 06:45 -->
|
||
|
|
<rect x="374" y="150" width="16" height="20" rx="1" fill="var(--chart-7)" opacity="0.8"/>
|
||
|
|
<rect x="374" y="156" width="16" height="14" rx="1" fill="var(--chart-2)" opacity="0.6"/>
|
||
|
|
|
||
|
|
<!-- 07:00 — peak errors -->
|
||
|
|
<rect x="396" y="130" width="16" height="40" rx="1" fill="var(--chart-7)" opacity="0.8"/>
|
||
|
|
<rect x="396" y="140" width="16" height="30" rx="1" fill="var(--chart-2)" opacity="0.6"/>
|
||
|
|
<rect x="396" y="150" width="16" height="20" rx="1" fill="var(--chart-1)" opacity="0.5"/>
|
||
|
|
<rect x="396" y="158" width="16" height="12" rx="1" fill="var(--chart-3)" opacity="0.5"/>
|
||
|
|
|
||
|
|
<!-- 07:15 -->
|
||
|
|
<rect x="418" y="138" width="16" height="32" rx="1" fill="var(--chart-7)" opacity="0.8"/>
|
||
|
|
<rect x="418" y="146" width="16" height="24" rx="1" fill="var(--chart-2)" opacity="0.6"/>
|
||
|
|
<rect x="418" y="156" width="16" height="14" rx="1" fill="var(--chart-1)" opacity="0.5"/>
|
||
|
|
|
||
|
|
<!-- 07:30 -->
|
||
|
|
<rect x="440" y="142" width="16" height="28" rx="1" fill="var(--chart-7)" opacity="0.8"/>
|
||
|
|
<rect x="440" y="150" width="16" height="20" rx="1" fill="var(--chart-2)" opacity="0.6"/>
|
||
|
|
|
||
|
|
<!-- 07:45 -->
|
||
|
|
<rect x="462" y="148" width="16" height="22" rx="1" fill="var(--chart-7)" opacity="0.8"/>
|
||
|
|
<rect x="462" y="156" width="16" height="14" rx="1" fill="var(--chart-2)" opacity="0.6"/>
|
||
|
|
|
||
|
|
<!-- 08:00 -->
|
||
|
|
<rect x="484" y="144" width="16" height="26" rx="1" fill="var(--chart-7)" opacity="0.8"/>
|
||
|
|
<rect x="484" y="152" width="16" height="18" rx="1" fill="var(--chart-2)" opacity="0.6"/>
|
||
|
|
<rect x="484" y="160" width="16" height="10" rx="1" fill="var(--chart-1)" opacity="0.5"/>
|
||
|
|
|
||
|
|
<!-- 08:15 -->
|
||
|
|
<rect x="506" y="150" width="16" height="20" rx="1" fill="var(--chart-7)" opacity="0.8"/>
|
||
|
|
<rect x="506" y="158" width="16" height="12" rx="1" fill="var(--chart-2)" opacity="0.6"/>
|
||
|
|
|
||
|
|
<!-- 08:30 -->
|
||
|
|
<rect x="528" y="154" width="16" height="16" rx="1" fill="var(--chart-7)" opacity="0.8"/>
|
||
|
|
<rect x="528" y="160" width="16" height="10" rx="1" fill="var(--chart-2)" opacity="0.6"/>
|
||
|
|
|
||
|
|
<!-- 08:45 -->
|
||
|
|
<rect x="550" y="156" width="16" height="14" rx="1" fill="var(--chart-7)" opacity="0.8"/>
|
||
|
|
<rect x="550" y="162" width="16" height="8" rx="1" fill="var(--chart-2)" opacity="0.6"/>
|
||
|
|
|
||
|
|
<!-- 09:00 (current, partial) -->
|
||
|
|
<rect x="572" y="158" width="8" height="12" rx="1" fill="var(--chart-7)" opacity="0.8"/>
|
||
|
|
<rect x="572" y="162" width="8" height="8" rx="1" fill="var(--chart-2)" opacity="0.6"/>
|
||
|
|
|
||
|
|
<!-- Total errors overlay line -->
|
||
|
|
<polyline points="52,164 74,168 96,164 118,166 140,160 162,166 184,162 206,164 228,160 250,166 272,164 294,162 316,150 338,154 360,146 382,152 404,132 426,140 448,144 470,150 492,146 514,152 536,156 558,158 576,160" fill="none" stroke="var(--text-secondary)" stroke-width="1.5" stroke-dasharray="4 2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
|
|
</svg>
|
||
|
|
</div>
|
||
|
|
<div class="chart-legend">
|
||
|
|
<div class="legend-item"><div class="legend-dot" style="background:var(--chart-7);"></div>retry-handler</div>
|
||
|
|
<div class="legend-item"><div class="legend-dot" style="background:var(--chart-2);"></div>payment-flow</div>
|
||
|
|
<div class="legend-item"><div class="legend-dot" style="background:var(--chart-1);"></div>order-processing</div>
|
||
|
|
<div class="legend-item"><div class="legend-dot" style="background:var(--chart-3);"></div>shipment-notify</div>
|
||
|
|
<div class="legend-item"><div class="legend-dot" style="background:var(--text-secondary); height:1px; border-top: 1.5px dashed var(--text-secondary); width:12px;"></div>total (overlay)</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Chart 4: Exchange Volume Heatmap -->
|
||
|
|
<div class="chart-card">
|
||
|
|
<div class="chart-card-header">
|
||
|
|
<div>
|
||
|
|
<div class="chart-card-title">Exchange Volume Heatmap</div>
|
||
|
|
<div class="chart-card-subtitle">Hourly volume by route · last 6h</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="chart-body" style="padding: 12px 16px;">
|
||
|
|
<div class="heatmap-grid">
|
||
|
|
<!-- Row: order-processing -->
|
||
|
|
<div class="heatmap-row">
|
||
|
|
<div class="heatmap-label">order-proc</div>
|
||
|
|
<div class="heatmap-cells">
|
||
|
|
<div class="heatmap-cell heat-2" title="03:00 — 142"></div>
|
||
|
|
<div class="heatmap-cell heat-3" title="04:00 — 287"></div>
|
||
|
|
<div class="heatmap-cell heat-4" title="05:00 — 456"></div>
|
||
|
|
<div class="heatmap-cell heat-7" title="06:00 — 812"></div>
|
||
|
|
<div class="heatmap-cell heat-8" title="07:00 — 923"></div>
|
||
|
|
<div class="heatmap-cell heat-8" title="08:00 — 891"></div>
|
||
|
|
<div class="heatmap-cell heat-6" title="09:00 — 653 (partial)"></div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Row: payment-flow -->
|
||
|
|
<div class="heatmap-row">
|
||
|
|
<div class="heatmap-label">payment-flow</div>
|
||
|
|
<div class="heatmap-cells">
|
||
|
|
<div class="heatmap-cell heat-1" title="03:00 — 68"></div>
|
||
|
|
<div class="heatmap-cell heat-2" title="04:00 — 134"></div>
|
||
|
|
<div class="heatmap-cell heat-3" title="05:00 — 245"></div>
|
||
|
|
<div class="heatmap-cell heat-6" title="06:00 — 587"></div>
|
||
|
|
<div class="heatmap-cell heat-7" title="07:00 — 712"></div>
|
||
|
|
<div class="heatmap-cell heat-6" title="08:00 — 645"></div>
|
||
|
|
<div class="heatmap-cell heat-5" title="09:00 — 401 (partial)"></div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Row: shipment-notify -->
|
||
|
|
<div class="heatmap-row">
|
||
|
|
<div class="heatmap-label">ship-notify</div>
|
||
|
|
<div class="heatmap-cells">
|
||
|
|
<div class="heatmap-cell heat-1" title="03:00 — 52"></div>
|
||
|
|
<div class="heatmap-cell heat-2" title="04:00 — 118"></div>
|
||
|
|
<div class="heatmap-cell heat-3" title="05:00 — 210"></div>
|
||
|
|
<div class="heatmap-cell heat-5" title="06:00 — 478"></div>
|
||
|
|
<div class="heatmap-cell heat-6" title="07:00 — 534"></div>
|
||
|
|
<div class="heatmap-cell heat-6" title="08:00 — 521"></div>
|
||
|
|
<div class="heatmap-cell heat-4" title="09:00 — 341 (partial)"></div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Row: inventory-check -->
|
||
|
|
<div class="heatmap-row">
|
||
|
|
<div class="heatmap-label">inv-check</div>
|
||
|
|
<div class="heatmap-cells">
|
||
|
|
<div class="heatmap-cell heat-1" title="03:00 — 38"></div>
|
||
|
|
<div class="heatmap-cell heat-2" title="04:00 — 97"></div>
|
||
|
|
<div class="heatmap-cell heat-2" title="05:00 — 178"></div>
|
||
|
|
<div class="heatmap-cell heat-5" title="06:00 — 412"></div>
|
||
|
|
<div class="heatmap-cell heat-5" title="07:00 — 467"></div>
|
||
|
|
<div class="heatmap-cell heat-5" title="08:00 — 445"></div>
|
||
|
|
<div class="heatmap-cell heat-4" title="09:00 — 298 (partial)"></div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Row: customer-validation -->
|
||
|
|
<div class="heatmap-row">
|
||
|
|
<div class="heatmap-label">cust-valid</div>
|
||
|
|
<div class="heatmap-cells">
|
||
|
|
<div class="heatmap-cell heat-1" title="03:00 — 24"></div>
|
||
|
|
<div class="heatmap-cell heat-1" title="04:00 — 76"></div>
|
||
|
|
<div class="heatmap-cell heat-2" title="05:00 — 145"></div>
|
||
|
|
<div class="heatmap-cell heat-4" title="06:00 — 356"></div>
|
||
|
|
<div class="heatmap-cell heat-5" title="07:00 — 398"></div>
|
||
|
|
<div class="heatmap-cell heat-4" title="08:00 — 367"></div>
|
||
|
|
<div class="heatmap-cell heat-3" title="09:00 — 234 (partial)"></div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Row: email-notification -->
|
||
|
|
<div class="heatmap-row">
|
||
|
|
<div class="heatmap-label">email-notif</div>
|
||
|
|
<div class="heatmap-cells">
|
||
|
|
<div class="heatmap-cell heat-0" title="03:00 — 8"></div>
|
||
|
|
<div class="heatmap-cell heat-1" title="04:00 — 42"></div>
|
||
|
|
<div class="heatmap-cell heat-1" title="05:00 — 78"></div>
|
||
|
|
<div class="heatmap-cell heat-3" title="06:00 — 245"></div>
|
||
|
|
<div class="heatmap-cell heat-4" title="07:00 — 312"></div>
|
||
|
|
<div class="heatmap-cell heat-3" title="08:00 — 278"></div>
|
||
|
|
<div class="heatmap-cell heat-2" title="09:00 — 167 (partial)"></div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Row: retry-handler -->
|
||
|
|
<div class="heatmap-row">
|
||
|
|
<div class="heatmap-label">retry-hdlr</div>
|
||
|
|
<div class="heatmap-cells">
|
||
|
|
<div class="heatmap-cell heat-0" title="03:00 — 4"></div>
|
||
|
|
<div class="heatmap-cell heat-1" title="04:00 — 12"></div>
|
||
|
|
<div class="heatmap-cell heat-1" title="05:00 — 18"></div>
|
||
|
|
<div class="heatmap-cell heat-2" title="06:00 — 89"></div>
|
||
|
|
<div class="heatmap-cell heat-3" title="07:00 — 134"></div>
|
||
|
|
<div class="heatmap-cell heat-2" title="08:00 — 112"></div>
|
||
|
|
<div class="heatmap-cell heat-1" title="09:00 — 67 (partial)"></div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Row: dead-letter-processor -->
|
||
|
|
<div class="heatmap-row">
|
||
|
|
<div class="heatmap-label" style="color:var(--text-faint);">dead-letter</div>
|
||
|
|
<div class="heatmap-cells">
|
||
|
|
<div class="heatmap-cell heat-0" title="03:00 — 0"></div>
|
||
|
|
<div class="heatmap-cell heat-0" title="04:00 — 0"></div>
|
||
|
|
<div class="heatmap-cell heat-0" title="05:00 — 0"></div>
|
||
|
|
<div class="heatmap-cell heat-0" title="06:00 — 0"></div>
|
||
|
|
<div class="heatmap-cell heat-0" title="07:00 — 0"></div>
|
||
|
|
<div class="heatmap-cell heat-0" title="08:00 — 0"></div>
|
||
|
|
<div class="heatmap-cell heat-0" title="09:00 — 0"></div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="heatmap-hour-labels">
|
||
|
|
<div class="heatmap-hour-label">03:00</div>
|
||
|
|
<div class="heatmap-hour-label">04:00</div>
|
||
|
|
<div class="heatmap-hour-label">05:00</div>
|
||
|
|
<div class="heatmap-hour-label">06:00</div>
|
||
|
|
<div class="heatmap-hour-label">07:00</div>
|
||
|
|
<div class="heatmap-hour-label">08:00</div>
|
||
|
|
<div class="heatmap-hour-label">09:00</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="heatmap-scale">
|
||
|
|
<span class="heatmap-scale-label">Low</span>
|
||
|
|
<div class="heatmap-scale-cells">
|
||
|
|
<div class="heatmap-scale-cell heat-0"></div>
|
||
|
|
<div class="heatmap-scale-cell heat-1"></div>
|
||
|
|
<div class="heatmap-scale-cell heat-2"></div>
|
||
|
|
<div class="heatmap-scale-cell heat-3"></div>
|
||
|
|
<div class="heatmap-scale-cell heat-4"></div>
|
||
|
|
<div class="heatmap-scale-cell heat-5"></div>
|
||
|
|
<div class="heatmap-scale-cell heat-6"></div>
|
||
|
|
<div class="heatmap-scale-cell heat-7"></div>
|
||
|
|
<div class="heatmap-scale-cell heat-8"></div>
|
||
|
|
</div>
|
||
|
|
<span class="heatmap-scale-label">High</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- ================================================================
|
||
|
|
LIVE EVENT FEED
|
||
|
|
================================================================ -->
|
||
|
|
<div class="live-feed-section animate-in delay-4">
|
||
|
|
<div class="live-feed-header">
|
||
|
|
<div class="live-feed-title">
|
||
|
|
<span class="live-feed-dot"></span>
|
||
|
|
Live Event Feed
|
||
|
|
</div>
|
||
|
|
<span class="table-meta">Last 25 events</span>
|
||
|
|
</div>
|
||
|
|
<div class="live-feed-list">
|
||
|
|
<div class="feed-item">
|
||
|
|
<div class="feed-icon feed-error">⚠</div>
|
||
|
|
<div class="feed-content">
|
||
|
|
<div class="feed-text"><strong>Threshold breached</strong> — P99 latency exceeded SLA on <span class="feed-route">payment-flow</span> (312ms > 300ms)</div>
|
||
|
|
<div class="feed-meta">09:12:44 · 2 min ago</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="feed-item">
|
||
|
|
<div class="feed-icon feed-error">✕</div>
|
||
|
|
<div class="feed-content">
|
||
|
|
<div class="feed-text"><strong>Error</strong> — <span class="feed-route">retry-handler</span> exhausted retries: CamelExecutionException after 3 attempts</div>
|
||
|
|
<div class="feed-meta">09:11:18 · 3 min ago</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="feed-item">
|
||
|
|
<div class="feed-icon feed-warn">△</div>
|
||
|
|
<div class="feed-content">
|
||
|
|
<div class="feed-text"><strong>Warning</strong> — <span class="feed-route">payment-flow</span> latency spike: P95 jumped from 198ms to 245ms</div>
|
||
|
|
<div class="feed-meta">09:08:32 · 6 min ago</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="feed-item">
|
||
|
|
<div class="feed-icon feed-error">✕</div>
|
||
|
|
<div class="feed-content">
|
||
|
|
<div class="feed-text"><strong>Error</strong> — <span class="feed-route">payment-flow</span> HTTP 504 from payment gateway (timeout after 5000ms)</div>
|
||
|
|
<div class="feed-meta">09:06:11 · 8 min ago</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="feed-item">
|
||
|
|
<div class="feed-icon feed-ok">✓</div>
|
||
|
|
<div class="feed-content">
|
||
|
|
<div class="feed-text"><strong>Recovery</strong> — <span class="feed-route">order-processing</span> error rate returned below 1% threshold</div>
|
||
|
|
<div class="feed-meta">09:04:55 · 10 min ago</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="feed-item">
|
||
|
|
<div class="feed-icon feed-info">ⓘ</div>
|
||
|
|
<div class="feed-content">
|
||
|
|
<div class="feed-text"><strong>In-flight high-water</strong> — 67 concurrent exchanges (capacity 78%). Auto-scaled connection pool.</div>
|
||
|
|
<div class="feed-meta">07:22:10 · 1h 52m ago</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="feed-item">
|
||
|
|
<div class="feed-icon feed-info">▶</div>
|
||
|
|
<div class="feed-content">
|
||
|
|
<div class="feed-text"><strong>Shift started</strong> — Day shift (06:00-18:00). Metrics counters reset.</div>
|
||
|
|
<div class="feed-meta">06:00:00 · 3h 14m ago</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="feed-item">
|
||
|
|
<div class="feed-icon feed-warn">△</div>
|
||
|
|
<div class="feed-content">
|
||
|
|
<div class="feed-text"><strong>Route stopped</strong> — <span class="feed-route">dead-letter-processor</span> manually stopped by operator hendrik</div>
|
||
|
|
<div class="feed-meta">05:48:22 · 3h 26m ago</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div><!-- /content -->
|
||
|
|
</div><!-- /main -->
|
||
|
|
|
||
|
|
</div><!-- /app -->
|
||
|
|
</body>
|
||
|
|
</html>
|