Files
cameleer-server/docs/ui-mocks/mock-operator-expert.html

1652 lines
49 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cameleer — Operations Console</title>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
/*
* ============================================================================
* CAMELEER OPERATIONS CONSOLE — DESIGN RATIONALE
* ============================================================================
*
* This mock is designed from 15 years of middleware operations experience.
* Here is what typical monitoring UIs get WRONG and how this addresses it:
*
* 1. BUSINESS CONTEXT IS KING
* Most monitoring tools show exchangeId, correlationId, routeId — all
* technical identifiers that mean nothing when a business user calls and
* says "order OP-88421 didn't go through." This console surfaces business
* IDs (Order ID, Customer ID, Source System) as first-class columns,
* extracted from exchange headers/properties during ingestion.
*
* 2. TIME-RELATIVE DISPLAY
* "2 min ago" is instantly parseable; "09:14:33.127" requires mental math.
* Recent items use relative time, older items switch to absolute. Both are
* shown on hover for precision when needed (e.g., incident reports).
*
* 3. ERRORS WITHOUT CLICKING
* The #1 time-waster in incident response: clicking through 40 failed
* executions one by one to read error messages. This table shows error
* previews inline and expands to full stack trace without navigation.
*
* 4. SLA AWARENESS
* Operators don't care that p99 latency is 342ms. They care that it's
* ABOVE the 300ms SLA threshold. Color-coding and threshold lines on
* charts make SLA violations visible at a glance.
*
* 5. TREND DIRECTION
* A metric value alone is useless without context. "Error rate: 2.3%"
* — is that going up or down? Trend arrows answer the only question
* that matters: "Is it getting better or worse?"
*
* 6. AGENT HEALTH CORRELATION
* When errors spike, the first question is "is it the app or the infra?"
* Seeing agent health alongside execution data answers this immediately.
* Dead/stale agents explain missing data; overloaded agents explain latency.
*
* 7. SHIFT-AWARE SUMMARIES
* Operators work in shifts. "Overnight failures" is a real concept.
* The health bar shows "since last shift" counts, not just "last 24h."
*
* 8. WHAT'S MISSING FROM THIS MOCK (would need backend support):
* - Reprocessing/retry actions (needs agent command channel)
* - Correlation chains (order -> payment -> shipment linked view)
* - Time-shifted comparison overlays (today vs yesterday)
* - Saved filter presets per operator
* - Alert rule configuration
* - Export to CSV/PDF for incident reports
* - Annotation markers on charts (deploys, config changes)
* - Multi-tenant/team-scoped views
* ============================================================================
*/
/* ─── Reset & Base ─── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--bg-deep: #060a13;
--bg-base: #0a0e17;
--bg-surface: #111827;
--bg-raised: #1a2332;
--bg-hover: #1e2d3d;
--border: #1e2d3d;
--border-subtle: #152030;
--text-primary: #e2e8f0;
--text-secondary: #8b9cb6;
--text-muted: #4a5e7a;
--amber: #f0b429;
--amber-dim: #b8860b;
--amber-glow: rgba(240, 180, 41, 0.15);
--cyan: #22d3ee;
--cyan-dim: #0e7490;
--cyan-glow: rgba(34, 211, 238, 0.12);
--rose: #f43f5e;
--rose-dim: #9f1239;
--rose-glow: rgba(244, 63, 94, 0.12);
--green: #10b981;
--green-glow: rgba(16, 185, 129, 0.12);
--blue: #3b82f6;
--purple: #a855f7;
--font-body: 'DM Sans', system-ui, sans-serif;
--font-mono: 'JetBrains Mono', 'Fira Code', monospace;
--radius-sm: 6px;
--radius-md: 10px;
--radius-lg: 14px;
}
body {
background: var(--bg-deep);
color: var(--text-primary);
font-family: var(--font-body);
font-size: 14px;
line-height: 1.5;
min-height: 100vh;
overflow-x: hidden;
}
body::before {
content: '';
position: fixed;
inset: 0;
background:
radial-gradient(ellipse 800px 400px at 20% 20%, rgba(240, 180, 41, 0.03), transparent),
radial-gradient(ellipse 600px 600px at 80% 80%, rgba(34, 211, 238, 0.02), transparent);
pointer-events: none;
z-index: 0;
}
::-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-muted); }
/* ─── Layout ─── */
.console {
position: relative;
z-index: 1;
max-width: 1920px;
margin: 0 auto;
padding: 0 24px 24px;
}
/* ─── Top Nav ─── */
.topnav {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 24px;
background: rgba(6, 10, 19, 0.85);
backdrop-filter: blur(12px);
border-bottom: 1px solid var(--border-subtle);
position: sticky;
top: 0;
z-index: 100;
margin: 0 -24px 20px;
}
.topnav-brand {
display: flex;
align-items: center;
gap: 10px;
}
.topnav-logo {
font-family: var(--font-mono);
font-size: 16px;
font-weight: 600;
color: var(--amber);
letter-spacing: -0.5px;
}
.topnav-sep {
color: var(--text-muted);
font-size: 18px;
font-weight: 300;
}
.topnav-page {
font-size: 14px;
font-weight: 500;
color: var(--text-secondary);
}
.topnav-right {
display: flex;
align-items: center;
gap: 16px;
}
.topnav-clock {
font-family: var(--font-mono);
font-size: 13px;
color: var(--text-muted);
}
.topnav-shift {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--cyan);
padding: 3px 10px;
border: 1px solid var(--cyan-dim);
border-radius: 99px;
background: var(--cyan-glow);
}
.live-indicator {
display: flex;
align-items: center;
gap: 6px;
font-family: var(--font-mono);
font-size: 12px;
font-weight: 500;
color: var(--green);
}
.live-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--green);
animation: livePulse 2s ease-in-out infinite;
}
@keyframes livePulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.4); }
50% { box-shadow: 0 0 0 6px rgba(16, 185, 129, 0); }
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
.animate-in { animation: fadeIn 0.3s ease-out both; }
/* ─── Health / SLA Status Bar ─── */
.health-bar {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 12px;
margin-bottom: 20px;
}
.health-card {
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-md);
padding: 14px 16px;
position: relative;
overflow: hidden;
}
.health-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
}
.health-card.ok::before { background: var(--green); }
.health-card.warn::before { background: var(--amber); }
.health-card.error::before { background: var(--rose); }
.health-card.info::before { background: var(--cyan); }
.health-label {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--text-muted);
margin-bottom: 6px;
}
.health-value-row {
display: flex;
align-items: baseline;
gap: 8px;
}
.health-value {
font-family: var(--font-mono);
font-size: 24px;
font-weight: 600;
}
.health-value.ok { color: var(--green); }
.health-value.warn { color: var(--amber); }
.health-value.error { color: var(--rose); }
.health-value.info { color: var(--cyan); }
.health-value.neutral { color: var(--text-primary); }
.health-unit {
font-size: 12px;
color: var(--text-muted);
}
.health-trend {
font-size: 12px;
font-family: var(--font-mono);
font-weight: 500;
display: inline-flex;
align-items: center;
gap: 2px;
margin-left: auto;
}
.health-trend.up-good { color: var(--green); }
.health-trend.up-bad { color: var(--rose); }
.health-trend.down-good { color: var(--green); }
.health-trend.down-bad { color: var(--rose); }
.health-trend.flat { color: var(--text-muted); }
.health-detail {
font-size: 11px;
color: var(--text-muted);
margin-top: 4px;
}
.health-detail strong {
color: var(--text-secondary);
}
/* ─── Summary Badges Row ─── */
.summary-row {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 16px;
flex-wrap: wrap;
}
.summary-badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 5px 14px;
border-radius: 99px;
font-family: var(--font-mono);
font-size: 13px;
font-weight: 500;
border: 1px solid;
}
.summary-badge.ok {
color: var(--green);
border-color: rgba(16, 185, 129, 0.3);
background: var(--green-glow);
}
.summary-badge.warn {
color: var(--amber);
border-color: rgba(240, 180, 41, 0.3);
background: var(--amber-glow);
}
.summary-badge.error {
color: var(--rose);
border-color: rgba(244, 63, 94, 0.3);
background: var(--rose-glow);
}
.summary-badge-dot {
width: 6px;
height: 6px;
border-radius: 50%;
}
.summary-badge.ok .summary-badge-dot { background: var(--green); }
.summary-badge.warn .summary-badge-dot { background: var(--amber); }
.summary-badge.error .summary-badge-dot { background: var(--rose); }
.summary-sep {
flex: 1;
}
.summary-timerange {
font-size: 12px;
color: var(--text-muted);
font-family: var(--font-mono);
}
/* ─── Two-Column Layout ─── */
.main-grid {
display: grid;
grid-template-columns: 1fr 320px;
gap: 16px;
}
/* ─── Filter / Search Bar ─── */
.filter-bar {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 12px;
flex-wrap: wrap;
}
.search-box {
flex: 1;
min-width: 240px;
display: flex;
align-items: center;
gap: 8px;
background: var(--bg-surface);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 8px 12px;
color: var(--text-primary);
font-size: 13px;
}
.search-box input {
flex: 1;
background: none;
border: none;
outline: none;
color: var(--text-primary);
font-family: var(--font-body);
font-size: 13px;
}
.search-box input::placeholder { color: var(--text-muted); }
.search-icon { color: var(--text-muted); font-size: 14px; }
.filter-chip {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 6px 12px;
border: 1px solid var(--border);
border-radius: 99px;
background: none;
color: var(--text-secondary);
font-size: 12px;
cursor: pointer;
transition: all 0.15s;
white-space: nowrap;
}
.filter-chip:hover {
background: var(--bg-raised);
border-color: var(--text-muted);
}
.filter-chip.active {
background: var(--amber-glow);
color: var(--amber);
border-color: rgba(240, 180, 41, 0.3);
}
.filter-chip .chip-count {
font-family: var(--font-mono);
font-weight: 600;
font-size: 11px;
}
.filter-chip.active-error {
background: var(--rose-glow);
color: var(--rose);
border-color: rgba(244, 63, 94, 0.3);
}
/* ─── Execution Table ─── */
.table-wrap {
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-lg);
overflow: hidden;
}
.exec-table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
}
.exec-table thead {
background: var(--bg-raised);
border-bottom: 1px solid var(--border);
}
.exec-table th {
padding: 10px 14px;
text-align: left;
font-size: 11px;
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.15s;
}
.exec-table th:hover { color: var(--text-secondary); }
.exec-table td {
padding: 10px 14px;
vertical-align: top;
white-space: nowrap;
}
.exec-table tbody tr {
border-bottom: 1px solid var(--border-subtle);
transition: background 0.1s;
cursor: pointer;
}
.exec-table tbody tr:last-child { border-bottom: none; }
.exec-table tbody tr:hover { background: var(--bg-raised); }
/* Status indicator strip on left */
.exec-table tbody tr {
border-left: 3px solid transparent;
}
.exec-table tbody tr.status-ok { border-left-color: var(--green); }
.exec-table tbody tr.status-warn { border-left-color: var(--amber); }
.exec-table tbody tr.status-error { border-left-color: var(--rose); }
/* Status pill */
.status-pill {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 3px 10px;
border-radius: 99px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.3px;
}
.status-pill.ok {
background: var(--green-glow);
color: var(--green);
}
.status-pill.warn {
background: var(--amber-glow);
color: var(--amber);
}
.status-pill.error {
background: var(--rose-glow);
color: var(--rose);
}
.status-pill-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: currentColor;
}
/* SLA violation marker */
.sla-breach {
display: inline-flex;
align-items: center;
gap: 3px;
color: var(--rose);
font-size: 11px;
font-family: var(--font-mono);
font-weight: 500;
}
.sla-breach::before {
content: '';
display: inline-block;
width: 0;
height: 0;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-bottom: 7px solid var(--rose);
}
/* Duration with SLA color */
.duration {
font-family: var(--font-mono);
font-size: 12px;
}
.duration.fast { color: var(--green); }
.duration.normal { color: var(--text-primary); }
.duration.slow { color: var(--amber); }
.duration.breach { color: var(--rose); font-weight: 600; }
/* Business ID styling */
.biz-id {
font-family: var(--font-mono);
font-weight: 500;
color: var(--cyan);
font-size: 12px;
}
.biz-id-secondary {
font-family: var(--font-mono);
font-size: 11px;
color: var(--text-muted);
}
/* Route name */
.route-name {
font-family: var(--font-mono);
font-size: 12px;
color: var(--text-primary);
}
.route-group {
font-size: 11px;
color: var(--text-muted);
}
/* Time display */
.time-relative {
color: var(--text-secondary);
font-size: 13px;
}
.time-absolute {
font-family: var(--font-mono);
font-size: 11px;
color: var(--text-muted);
}
/* Error preview row */
.error-preview-row td {
padding: 0 14px 10px 20px !important;
border-bottom: 1px solid var(--border-subtle);
}
.error-preview {
display: flex;
align-items: flex-start;
gap: 8px;
padding: 8px 12px;
background: rgba(244, 63, 94, 0.06);
border: 1px solid rgba(244, 63, 94, 0.15);
border-radius: var(--radius-sm);
font-family: var(--font-mono);
font-size: 11px;
color: var(--rose);
line-height: 1.6;
max-width: 100%;
overflow: hidden;
}
.error-preview-icon {
flex-shrink: 0;
margin-top: 1px;
}
.error-preview-text {
white-space: normal;
word-break: break-word;
}
.error-expand-hint {
color: var(--text-muted);
font-size: 10px;
margin-top: 4px;
font-family: var(--font-body);
}
/* Agent badge in table */
.agent-badge {
display: inline-flex;
align-items: center;
gap: 4px;
font-family: var(--font-mono);
font-size: 11px;
color: var(--text-muted);
}
.agent-badge-dot {
width: 6px;
height: 6px;
border-radius: 50%;
}
.agent-badge-dot.live { background: var(--green); }
.agent-badge-dot.stale { background: var(--amber); }
.agent-badge-dot.dead { background: var(--rose); }
/* ─── Right Sidebar ─── */
.sidebar {
display: flex;
flex-direction: column;
gap: 16px;
}
/* ─── Agent Health Panel ─── */
.panel {
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-md);
overflow: hidden;
}
.panel-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
border-bottom: 1px solid var(--border-subtle);
}
.panel-title {
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--text-muted);
}
.panel-badge {
font-family: var(--font-mono);
font-size: 11px;
padding: 2px 8px;
border-radius: 99px;
}
.panel-body {
padding: 12px 16px;
}
/* Agent list */
.agent-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.agent-item {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
background: var(--bg-raised);
border-radius: var(--radius-sm);
border: 1px solid var(--border-subtle);
transition: border-color 0.15s;
}
.agent-item:hover { border-color: var(--border); }
.agent-status-dot {
width: 10px;
height: 10px;
border-radius: 50%;
flex-shrink: 0;
}
.agent-status-dot.live {
background: var(--green);
box-shadow: 0 0 6px rgba(16, 185, 129, 0.4);
}
.agent-status-dot.stale {
background: var(--amber);
box-shadow: 0 0 6px rgba(240, 180, 41, 0.3);
}
.agent-status-dot.dead {
background: var(--rose);
opacity: 0.6;
}
.agent-info { flex: 1; min-width: 0; }
.agent-name {
font-family: var(--font-mono);
font-size: 12px;
font-weight: 500;
color: var(--text-primary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.agent-meta {
font-size: 11px;
color: var(--text-muted);
display: flex;
gap: 8px;
margin-top: 2px;
}
.agent-stats {
text-align: right;
font-family: var(--font-mono);
font-size: 11px;
}
.agent-tps { color: var(--text-secondary); }
.agent-errors { color: var(--rose); }
.agent-heartbeat { font-size: 10px; color: var(--text-muted); }
/* ─── Metrics Panels ─── */
.metric-chart {
padding: 16px;
}
.metric-header {
display: flex;
align-items: baseline;
justify-content: space-between;
margin-bottom: 12px;
}
.metric-title {
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--text-muted);
}
.metric-current {
font-family: var(--font-mono);
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
}
.metric-current .trend-arrow {
font-size: 14px;
margin-left: 4px;
}
.metric-current .trend-arrow.up-bad { color: var(--rose); }
.metric-current .trend-arrow.down-good { color: var(--green); }
.metric-current .trend-arrow.up-good { color: var(--green); }
.metric-current .trend-arrow.flat { color: var(--text-muted); }
/* SVG Chart area */
.chart-area {
width: 100%;
height: 80px;
position: relative;
}
.chart-area svg {
width: 100%;
height: 100%;
}
/* Sparkline-style mini charts */
.sparkline {
display: block;
}
/* SLA threshold line */
.sla-line {
stroke: var(--rose);
stroke-width: 1;
stroke-dasharray: 4 3;
opacity: 0.6;
}
.sla-label {
fill: var(--rose);
font-size: 9px;
font-family: 'JetBrains Mono', monospace;
opacity: 0.7;
}
/* ─── Top Errors Panel ─── */
.error-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.error-item {
display: flex;
align-items: flex-start;
gap: 10px;
padding: 8px 10px;
background: var(--bg-raised);
border-radius: var(--radius-sm);
border-left: 3px solid var(--rose);
}
.error-count {
font-family: var(--font-mono);
font-size: 14px;
font-weight: 600;
color: var(--rose);
min-width: 28px;
text-align: center;
}
.error-info { flex: 1; min-width: 0; }
.error-route {
font-family: var(--font-mono);
font-size: 11px;
font-weight: 500;
color: var(--text-secondary);
}
.error-message {
font-size: 11px;
color: var(--text-muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-top: 2px;
}
.error-since {
font-size: 10px;
color: var(--text-muted);
margin-top: 2px;
}
/* ─── Table footer / pagination ─── */
.table-footer {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 14px;
border-top: 1px solid var(--border-subtle);
font-size: 12px;
color: var(--text-muted);
background: var(--bg-raised);
}
.table-footer-info {
font-family: var(--font-mono);
font-size: 11px;
}
.pagination {
display: flex;
gap: 4px;
}
.page-btn {
padding: 4px 10px;
border: 1px solid var(--border);
border-radius: var(--radius-sm);
background: none;
color: var(--text-secondary);
font-size: 12px;
cursor: pointer;
transition: all 0.15s;
}
.page-btn:hover {
background: var(--bg-hover);
color: var(--text-primary);
}
.page-btn.active {
background: var(--amber-glow);
color: var(--amber);
border-color: rgba(240, 180, 41, 0.3);
}
/* ─── Utility ─── */
.mono { font-family: var(--font-mono); }
.flex-center { display: flex; align-items: center; }
/* ─── Chart fill colors ─── */
.fill-green { fill: rgba(16, 185, 129, 0.15); }
.stroke-green { stroke: var(--green); }
.fill-amber { fill: rgba(240, 180, 41, 0.1); }
.stroke-amber { stroke: var(--amber); }
.fill-cyan { fill: rgba(34, 211, 238, 0.1); }
.stroke-cyan { stroke: var(--cyan); }
.fill-rose { fill: rgba(244, 63, 94, 0.1); }
.stroke-rose { stroke: var(--rose); }
</style>
</head>
<body>
<div class="console">
<!-- ─── Top Navigation ─── -->
<nav class="topnav">
<div class="topnav-brand">
<span class="topnav-logo">cameleer</span>
<span class="topnav-sep">/</span>
<span class="topnav-page">Operations Console</span>
</div>
<div class="topnav-right">
<span class="topnav-shift">Shift: Day (06:00-18:00)</span>
<span class="topnav-clock">2026-03-17 09:14 CET</span>
<span class="live-indicator">
<span class="live-dot"></span>
LIVE
</span>
</div>
</nav>
<!-- ─── Health / SLA Status Bar ─── -->
<div class="health-bar animate-in">
<div class="health-card ok">
<div class="health-label">System Health</div>
<div class="health-value-row">
<span class="health-value ok">HEALTHY</span>
</div>
<div class="health-detail">All 4 agents reporting &middot; No SLA breaches</div>
</div>
<div class="health-card ok">
<div class="health-label">Executions (shift)</div>
<div class="health-value-row">
<span class="health-value neutral">3,241</span>
<span class="health-trend up-good">+12%</span>
</div>
<div class="health-detail"><strong>97.1%</strong> success rate since 06:00</div>
</div>
<div class="health-card warn">
<div class="health-label">Failures (shift)</div>
<div class="health-value-row">
<span class="health-value error">38</span>
<span class="health-trend up-bad">+5</span>
</div>
<div class="health-detail"><strong>23</strong> overnight &middot; <strong>15</strong> since 06:00</div>
</div>
<div class="health-card ok">
<div class="health-label">Throughput</div>
<div class="health-value-row">
<span class="health-value neutral">47.2</span>
<span class="health-unit">msg/s</span>
<span class="health-trend flat">&#8596;</span>
</div>
<div class="health-detail">Capacity: 120 msg/s &middot; <strong>39%</strong> utilized</div>
</div>
<div class="health-card ok">
<div class="health-label">Latency p50</div>
<div class="health-value-row">
<span class="health-value neutral">142</span>
<span class="health-unit">ms</span>
<span class="health-trend down-good">-8ms</span>
</div>
<div class="health-detail">SLA: &lt;200ms &middot; <strong style="color: var(--green);">OK</strong></div>
</div>
<div class="health-card warn">
<div class="health-label">Latency p99</div>
<div class="health-value-row">
<span class="health-value warn">287</span>
<span class="health-unit">ms</span>
<span class="health-trend up-bad">+23ms</span>
</div>
<div class="health-detail">SLA: &lt;300ms &middot; <strong style="color: var(--amber);">CLOSE</strong></div>
</div>
</div>
<!-- ─── Summary Badges ─── -->
<div class="summary-row animate-in" style="animation-delay: 0.05s">
<span class="summary-badge ok">
<span class="summary-badge-dot"></span>
3,197 ok
</span>
<span class="summary-badge warn">
<span class="summary-badge-dot"></span>
6 warn
</span>
<span class="summary-badge error">
<span class="summary-badge-dot"></span>
38 error
</span>
<span class="summary-sep"></span>
<span class="summary-timerange">Last 12h &middot; All groups</span>
</div>
<!-- ─── Main Grid: Table + Sidebar ─── -->
<div class="main-grid">
<!-- ─── Left: Execution Table ─── -->
<div class="animate-in" style="animation-delay: 0.1s">
<!-- Filter Bar -->
<div class="filter-bar">
<div class="search-box">
<span class="search-icon">&#128269;</span>
<input type="text" placeholder="Search by Order ID, Customer ID, Correlation ID, Error message...">
</div>
<button class="filter-chip active">All Routes</button>
<button class="filter-chip">order-flow <span class="chip-count">1,847</span></button>
<button class="filter-chip">payment-flow <span class="chip-count">923</span></button>
<button class="filter-chip">shipment-flow <span class="chip-count">471</span></button>
<button class="filter-chip active-error">Errors only <span class="chip-count">38</span></button>
</div>
<!-- Execution Table -->
<div class="table-wrap">
<table class="exec-table">
<thead>
<tr>
<th style="width: 30px;"></th>
<th>Status</th>
<th>Route / Group</th>
<th>Order ID</th>
<th>Customer</th>
<th>Started</th>
<th>Duration</th>
<th>Agent</th>
</tr>
</thead>
<tbody>
<!-- Row 1: Recent success -->
<tr class="status-ok">
<td></td>
<td><span class="status-pill ok"><span class="status-pill-dot"></span>OK</span></td>
<td>
<div class="route-name">order-intake</div>
<div class="route-group">order-flow</div>
</td>
<td>
<div class="biz-id">OP-92184</div>
<div class="biz-id-secondary">SAP-DE</div>
</td>
<td>
<div class="biz-id-secondary">CUST-44210</div>
</td>
<td>
<div class="time-relative">2 min ago</div>
<div class="time-absolute">09:12:04</div>
</td>
<td><span class="duration fast">87ms</span></td>
<td>
<span class="agent-badge"><span class="agent-badge-dot live"></span>prod-1</span>
</td>
</tr>
<!-- Row 2: Recent success, slightly slow -->
<tr class="status-ok">
<td></td>
<td><span class="status-pill ok"><span class="status-pill-dot"></span>OK</span></td>
<td>
<div class="route-name">payment-validate</div>
<div class="route-group">payment-flow</div>
</td>
<td>
<div class="biz-id">OP-92183</div>
<div class="biz-id-secondary">WEB-EU</div>
</td>
<td>
<div class="biz-id-secondary">CUST-18773</div>
</td>
<td>
<div class="time-relative">3 min ago</div>
<div class="time-absolute">09:11:22</div>
</td>
<td><span class="duration normal">164ms</span></td>
<td>
<span class="agent-badge"><span class="agent-badge-dot live"></span>prod-2</span>
</td>
</tr>
<!-- Row 3: WARNING - slow but succeeded -->
<tr class="status-warn">
<td></td>
<td><span class="status-pill warn"><span class="status-pill-dot"></span>WARN</span></td>
<td>
<div class="route-name">shipment-dispatch</div>
<div class="route-group">shipment-flow</div>
</td>
<td>
<div class="biz-id">OP-92180</div>
<div class="biz-id-secondary">SAP-DE</div>
</td>
<td>
<div class="biz-id-secondary">CUST-55019</div>
</td>
<td>
<div class="time-relative">5 min ago</div>
<div class="time-absolute">09:09:47</div>
</td>
<td>
<span class="duration slow">248ms</span>
<span class="sla-breach" title="Approaching SLA threshold (300ms)">SLA</span>
</td>
<td>
<span class="agent-badge"><span class="agent-badge-dot live"></span>prod-3</span>
</td>
</tr>
<!-- Row 4: ERROR with inline preview -->
<tr class="status-error">
<td></td>
<td><span class="status-pill error"><span class="status-pill-dot"></span>ERROR</span></td>
<td>
<div class="route-name">payment-process</div>
<div class="route-group">payment-flow</div>
</td>
<td>
<div class="biz-id">OP-88421</div>
<div class="biz-id-secondary">WEB-EU</div>
</td>
<td>
<div class="biz-id-secondary">CUST-31094</div>
</td>
<td>
<div class="time-relative">8 min ago</div>
<div class="time-absolute">09:06:11</div>
</td>
<td><span class="duration breach">412ms</span></td>
<td>
<span class="agent-badge"><span class="agent-badge-dot live"></span>prod-2</span>
</td>
</tr>
<!-- Error preview row (expanded inline) -->
<tr class="error-preview-row">
<td colspan="8">
<div class="error-preview">
<span class="error-preview-icon">&#9888;</span>
<div>
<div class="error-preview-text">org.apache.camel.CamelExecutionException: Payment gateway timeout after 5000ms &mdash; POST https://pay.provider.com/v2/charge returned HTTP 504. Retry exhausted (3/3 attempts). CorrelationId: c7e2a1f0-9b3d</div>
<div class="error-expand-hint">Click to view full stack trace and exchange snapshot</div>
</div>
</div>
</td>
</tr>
<!-- Row 5: Another error -->
<tr class="status-error">
<td></td>
<td><span class="status-pill error"><span class="status-pill-dot"></span>ERROR</span></td>
<td>
<div class="route-name">order-enrichment</div>
<div class="route-group">order-flow</div>
</td>
<td>
<div class="biz-id">OP-88419</div>
<div class="biz-id-secondary">EDI-US</div>
</td>
<td>
<div class="biz-id-secondary">CUST-72841</div>
</td>
<td>
<div class="time-relative">12 min ago</div>
<div class="time-absolute">09:02:38</div>
</td>
<td><span class="duration breach">1,247ms</span></td>
<td>
<span class="agent-badge"><span class="agent-badge-dot live"></span>prod-1</span>
</td>
</tr>
<tr class="error-preview-row">
<td colspan="8">
<div class="error-preview">
<span class="error-preview-icon">&#9888;</span>
<div>
<div class="error-preview-text">java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 1000ms. (pool size: 10, active: 10, idle: 0, waiting: 3)</div>
<div class="error-expand-hint">Click to view full stack trace and exchange snapshot</div>
</div>
</div>
</td>
</tr>
<!-- Row 6: Success -->
<tr class="status-ok">
<td></td>
<td><span class="status-pill ok"><span class="status-pill-dot"></span>OK</span></td>
<td>
<div class="route-name">order-intake</div>
<div class="route-group">order-flow</div>
</td>
<td>
<div class="biz-id">OP-92179</div>
<div class="biz-id-secondary">WEB-EU</div>
</td>
<td>
<div class="biz-id-secondary">CUST-90123</div>
</td>
<td>
<div class="time-relative">14 min ago</div>
<div class="time-absolute">09:00:15</div>
</td>
<td><span class="duration fast">62ms</span></td>
<td>
<span class="agent-badge"><span class="agent-badge-dot live"></span>prod-3</span>
</td>
</tr>
<!-- Row 7: Success -->
<tr class="status-ok">
<td></td>
<td><span class="status-pill ok"><span class="status-pill-dot"></span>OK</span></td>
<td>
<div class="route-name">payment-validate</div>
<div class="route-group">payment-flow</div>
</td>
<td>
<div class="biz-id">OP-92178</div>
<div class="biz-id-secondary">SAP-DE</div>
</td>
<td>
<div class="biz-id-secondary">CUST-44210</div>
</td>
<td>
<div class="time-relative">16 min ago</div>
<div class="time-absolute">08:58:33</div>
</td>
<td><span class="duration normal">131ms</span></td>
<td>
<span class="agent-badge"><span class="agent-badge-dot live"></span>prod-1</span>
</td>
</tr>
<!-- Row 8: Success -->
<tr class="status-ok">
<td></td>
<td><span class="status-pill ok"><span class="status-pill-dot"></span>OK</span></td>
<td>
<div class="route-name">shipment-track</div>
<div class="route-group">shipment-flow</div>
</td>
<td>
<div class="biz-id">OP-92175</div>
<div class="biz-id-secondary">EDI-US</div>
</td>
<td>
<div class="biz-id-secondary">CUST-67892</div>
</td>
<td>
<div class="time-relative">21 min ago</div>
<div class="time-absolute">08:53:07</div>
</td>
<td><span class="duration fast">94ms</span></td>
<td>
<span class="agent-badge"><span class="agent-badge-dot live"></span>prod-4</span>
</td>
</tr>
<!-- Row 9: Error from overnight -->
<tr class="status-error">
<td></td>
<td><span class="status-pill error"><span class="status-pill-dot"></span>ERROR</span></td>
<td>
<div class="route-name">payment-process</div>
<div class="route-group">payment-flow</div>
</td>
<td>
<div class="biz-id">OP-91844</div>
<div class="biz-id-secondary">WEB-EU</div>
</td>
<td>
<div class="biz-id-secondary">CUST-10082</div>
</td>
<td>
<div class="time-relative">5 hours ago</div>
<div class="time-absolute">04:12:55</div>
</td>
<td><span class="duration breach">timeout</span></td>
<td>
<span class="agent-badge"><span class="agent-badge-dot live"></span>prod-2</span>
</td>
</tr>
<tr class="error-preview-row">
<td colspan="8">
<div class="error-preview">
<span class="error-preview-icon">&#9888;</span>
<div>
<div class="error-preview-text">org.apache.camel.CamelExecutionException: Payment gateway timeout after 5000ms &mdash; POST https://pay.provider.com/v2/charge returned HTTP 504. Retry exhausted (3/3 attempts). Overnight batch: provider maintenance window?</div>
<div class="error-expand-hint">Click to view full stack trace and exchange snapshot</div>
</div>
</div>
</td>
</tr>
<!-- Row 10: Success -->
<tr class="status-ok">
<td></td>
<td><span class="status-pill ok"><span class="status-pill-dot"></span>OK</span></td>
<td>
<div class="route-name">order-intake</div>
<div class="route-group">order-flow</div>
</td>
<td>
<div class="biz-id">OP-91840</div>
<div class="biz-id-secondary">EDI-US</div>
</td>
<td>
<div class="biz-id-secondary">CUST-38291</div>
</td>
<td>
<div class="time-relative">5 hours ago</div>
<div class="time-absolute">04:10:22</div>
</td>
<td><span class="duration fast">78ms</span></td>
<td>
<span class="agent-badge"><span class="agent-badge-dot live"></span>prod-1</span>
</td>
</tr>
</tbody>
</table>
<div class="table-footer">
<div class="table-footer-info">
Showing 1-20 of 3,241 executions
</div>
<div class="pagination">
<button class="page-btn active">1</button>
<button class="page-btn">2</button>
<button class="page-btn">3</button>
<button class="page-btn">...</button>
<button class="page-btn">162</button>
</div>
</div>
</div>
</div>
<!-- ─── Right Sidebar ─── -->
<div class="sidebar animate-in" style="animation-delay: 0.15s">
<!-- Agent Health -->
<div class="panel">
<div class="panel-header">
<span class="panel-title">Agent Health</span>
<span class="panel-badge" style="color: var(--green); background: var(--green-glow); border: 1px solid rgba(16,185,129,0.3); border-radius: 99px;">4 / 4 live</span>
</div>
<div class="panel-body">
<div class="agent-list">
<div class="agent-item">
<span class="agent-status-dot live"></span>
<div class="agent-info">
<div class="agent-name">prod-1</div>
<div class="agent-meta">
<span>order-service</span>
<span>v3.2.1</span>
</div>
</div>
<div class="agent-stats">
<div class="agent-tps">14.2 msg/s</div>
<div class="agent-heartbeat">12s ago</div>
</div>
</div>
<div class="agent-item">
<span class="agent-status-dot live"></span>
<div class="agent-info">
<div class="agent-name">prod-2</div>
<div class="agent-meta">
<span>payment-service</span>
<span>v3.2.1</span>
</div>
</div>
<div class="agent-stats">
<div class="agent-tps">11.8 msg/s</div>
<div class="agent-errors">3 err/h</div>
<div class="agent-heartbeat">8s ago</div>
</div>
</div>
<div class="agent-item">
<span class="agent-status-dot live"></span>
<div class="agent-info">
<div class="agent-name">prod-3</div>
<div class="agent-meta">
<span>shipment-service</span>
<span>v3.2.0</span>
</div>
</div>
<div class="agent-stats">
<div class="agent-tps">12.1 msg/s</div>
<div class="agent-heartbeat">5s ago</div>
</div>
</div>
<div class="agent-item">
<span class="agent-status-dot live"></span>
<div class="agent-info">
<div class="agent-name">prod-4</div>
<div class="agent-meta">
<span>shipment-service</span>
<span>v3.2.0</span>
</div>
</div>
<div class="agent-stats">
<div class="agent-tps">9.1 msg/s</div>
<div class="agent-heartbeat">3s ago</div>
</div>
</div>
</div>
</div>
</div>
<!-- Throughput Chart -->
<div class="panel">
<div class="metric-chart">
<div class="metric-header">
<span class="metric-title">Throughput (1h)</span>
<span class="metric-current">47.2 <span class="trend-arrow flat">&#8596;</span></span>
</div>
<div class="chart-area">
<svg viewBox="0 0 288 80" preserveAspectRatio="none">
<!-- Grid lines -->
<line x1="0" y1="20" x2="288" y2="20" stroke="#1e2d3d" stroke-width="0.5"/>
<line x1="0" y1="40" x2="288" y2="40" stroke="#1e2d3d" stroke-width="0.5"/>
<line x1="0" y1="60" x2="288" y2="60" stroke="#1e2d3d" stroke-width="0.5"/>
<!-- Capacity line at ~33% (40 of 120) -->
<!-- Area fill -->
<path d="M0,55 L24,52 L48,48 L72,50 L96,45 L120,42 L144,44 L168,40 L192,38 L216,40 L240,42 L264,39 L288,38 L288,80 L0,80 Z" class="fill-cyan" />
<!-- Line -->
<path d="M0,55 L24,52 L48,48 L72,50 L96,45 L120,42 L144,44 L168,40 L192,38 L216,40 L240,42 L264,39 L288,38" fill="none" class="stroke-cyan" stroke-width="1.5"/>
<!-- Time labels -->
<text x="0" y="78" fill="#4a5e7a" font-size="8" font-family="JetBrains Mono">08:15</text>
<text x="138" y="78" fill="#4a5e7a" font-size="8" font-family="JetBrains Mono">08:45</text>
<text x="272" y="78" fill="#4a5e7a" font-size="8" font-family="JetBrains Mono">09:14</text>
<!-- Scale -->
<text x="278" y="18" fill="#4a5e7a" font-size="7" font-family="JetBrains Mono" text-anchor="end">80</text>
<text x="278" y="38" fill="#4a5e7a" font-size="7" font-family="JetBrains Mono" text-anchor="end">50</text>
<text x="278" y="58" fill="#4a5e7a" font-size="7" font-family="JetBrains Mono" text-anchor="end">20</text>
</svg>
</div>
</div>
</div>
<!-- Latency Chart with SLA line -->
<div class="panel">
<div class="metric-chart">
<div class="metric-header">
<span class="metric-title">Latency p99 (1h)</span>
<span class="metric-current" style="color: var(--amber);">287ms <span class="trend-arrow up-bad">&#8599;</span></span>
</div>
<div class="chart-area">
<svg viewBox="0 0 288 80" preserveAspectRatio="none">
<!-- Grid lines -->
<line x1="0" y1="20" x2="288" y2="20" stroke="#1e2d3d" stroke-width="0.5"/>
<line x1="0" y1="40" x2="288" y2="40" stroke="#1e2d3d" stroke-width="0.5"/>
<line x1="0" y1="60" x2="288" y2="60" stroke="#1e2d3d" stroke-width="0.5"/>
<!-- SLA threshold line at 300ms (~y=18 on our 0-350ms scale) -->
<line x1="0" y1="12" x2="288" y2="12" class="sla-line"/>
<text x="4" y="10" class="sla-label">SLA 300ms</text>
<!-- Danger zone fill above SLA -->
<rect x="0" y="0" width="288" height="12" fill="rgba(244, 63, 94, 0.04)"/>
<!-- Area fill -->
<path d="M0,48 L24,45 L48,42 L72,40 L96,38 L120,35 L144,32 L168,28 L192,24 L216,22 L240,20 L264,18 L288,16 L288,80 L0,80 Z" class="fill-amber" />
<!-- Line -->
<path d="M0,48 L24,45 L48,42 L72,40 L96,38 L120,35 L144,32 L168,28 L192,24 L216,22 L240,20 L264,18 L288,16" fill="none" class="stroke-amber" stroke-width="1.5"/>
<!-- p50 line (reference) -->
<path d="M0,60 L24,62 L48,60 L72,58 L96,60 L120,59 L144,58 L168,60 L192,58 L216,57 L240,58 L264,56 L288,55" fill="none" stroke="#22d3ee" stroke-width="1" opacity="0.5"/>
<text x="288" y="54" fill="#22d3ee" font-size="7" font-family="JetBrains Mono" text-anchor="end" opacity="0.6">p50</text>
<!-- Time labels -->
<text x="0" y="78" fill="#4a5e7a" font-size="8" font-family="JetBrains Mono">08:15</text>
<text x="138" y="78" fill="#4a5e7a" font-size="8" font-family="JetBrains Mono">08:45</text>
<text x="272" y="78" fill="#4a5e7a" font-size="8" font-family="JetBrains Mono">09:14</text>
<!-- Scale -->
<text x="278" y="18" fill="#4a5e7a" font-size="7" font-family="JetBrains Mono" text-anchor="end">300</text>
<text x="278" y="38" fill="#4a5e7a" font-size="7" font-family="JetBrains Mono" text-anchor="end">200</text>
<text x="278" y="58" fill="#4a5e7a" font-size="7" font-family="JetBrains Mono" text-anchor="end">100</text>
</svg>
</div>
</div>
</div>
<!-- Error Rate Chart -->
<div class="panel">
<div class="metric-chart">
<div class="metric-header">
<span class="metric-title">Error Rate (1h)</span>
<span class="metric-current" style="color: var(--rose);">2.9% <span class="trend-arrow up-bad">&#8599;</span></span>
</div>
<div class="chart-area">
<svg viewBox="0 0 288 80" preserveAspectRatio="none">
<!-- Grid lines -->
<line x1="0" y1="20" x2="288" y2="20" stroke="#1e2d3d" stroke-width="0.5"/>
<line x1="0" y1="40" x2="288" y2="40" stroke="#1e2d3d" stroke-width="0.5"/>
<line x1="0" y1="60" x2="288" y2="60" stroke="#1e2d3d" stroke-width="0.5"/>
<!-- SLA threshold at 5% (y=20) -->
<line x1="0" y1="20" x2="288" y2="20" class="sla-line"/>
<text x="4" y="18" class="sla-label">SLA 5%</text>
<rect x="0" y="0" width="288" height="20" fill="rgba(244, 63, 94, 0.04)"/>
<!-- Bar chart style for errors -->
<rect x="2" y="62" width="20" height="18" rx="2" fill="rgba(244, 63, 94, 0.3)"/>
<rect x="26" y="65" width="20" height="15" rx="2" fill="rgba(244, 63, 94, 0.3)"/>
<rect x="50" y="68" width="20" height="12" rx="2" fill="rgba(244, 63, 94, 0.3)"/>
<rect x="74" y="64" width="20" height="16" rx="2" fill="rgba(244, 63, 94, 0.3)"/>
<rect x="98" y="60" width="20" height="20" rx="2" fill="rgba(244, 63, 94, 0.3)"/>
<rect x="122" y="66" width="20" height="14" rx="2" fill="rgba(244, 63, 94, 0.3)"/>
<rect x="146" y="70" width="20" height="10" rx="2" fill="rgba(244, 63, 94, 0.3)"/>
<rect x="170" y="58" width="20" height="22" rx="2" fill="rgba(244, 63, 94, 0.4)"/>
<rect x="194" y="52" width="20" height="28" rx="2" fill="rgba(244, 63, 94, 0.5)"/>
<rect x="218" y="48" width="20" height="32" rx="2" fill="rgba(244, 63, 94, 0.6)"/>
<rect x="242" y="44" width="20" height="36" rx="2" fill="rgba(244, 63, 94, 0.7)"/>
<rect x="266" y="42" width="20" height="38" rx="2" fill="rgba(244, 63, 94, 0.8)"/>
<!-- Time labels -->
<text x="0" y="78" fill="#4a5e7a" font-size="8" font-family="JetBrains Mono">08:15</text>
<text x="138" y="78" fill="#4a5e7a" font-size="8" font-family="JetBrains Mono">08:45</text>
<text x="272" y="78" fill="#4a5e7a" font-size="8" font-family="JetBrains Mono">09:14</text>
</svg>
</div>
</div>
</div>
<!-- Top Errors Panel -->
<div class="panel">
<div class="panel-header">
<span class="panel-title">Top Errors (shift)</span>
<span class="panel-badge" style="color: var(--rose); background: var(--rose-glow); border: 1px solid rgba(244,63,94,0.3); border-radius: 99px;">3 patterns</span>
</div>
<div class="panel-body">
<div class="error-list">
<div class="error-item">
<div class="error-count">23</div>
<div class="error-info">
<div class="error-route">payment-flow / payment-process</div>
<div class="error-message">Payment gateway timeout (HTTP 504)</div>
<div class="error-since">First: 03:42 &middot; Last: 09:06</div>
</div>
</div>
<div class="error-item">
<div class="error-count">11</div>
<div class="error-info">
<div class="error-route">order-flow / order-enrichment</div>
<div class="error-message">HikariPool connection timeout</div>
<div class="error-since">First: 08:55 &middot; Last: 09:02</div>
</div>
</div>
<div class="error-item">
<div class="error-count">4</div>
<div class="error-info">
<div class="error-route">shipment-flow / shipment-dispatch</div>
<div class="error-message">NullPointerException at ShipmentMapper:142</div>
<div class="error-since">First: 07:11 &middot; Last: 08:34</div>
</div>
</div>
</div>
</div>
</div>
</div><!-- /sidebar -->
</div><!-- /main-grid -->
</div><!-- /console -->
</body>
</html>