refactor: rename group/groupName to application/applicationName
The execution-related "group" concept actually represents the
application name. Rename all Java fields, API parameters, and frontend
types from groupName→applicationName and group→application for clarity.
- Java records: ExecutionSummary, ExecutionDetail, ExecutionDocument,
ExecutionRecord, ProcessorRecord
- API params: SearchRequest.group→application, SearchController
@RequestParam group→application
- Services: IngestionService, DetailService, SearchIndexer, StatsStore
- Frontend: schema.d.ts, Dashboard, ExchangeDetail, RouteDetail,
executions query hooks
Database column names (group_name) and OpenSearch field names are
unchanged — only the API-facing Java/TS field names are renamed.
RBAC group references (groups table, GroupRepository, GroupsTab) are
a separate domain concept and are NOT affected by this change.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 21:21:38 +01:00
<!--
============================================================================
2026-04-15 15:28:42 +02:00
CAMELEER v3 — Agent Health & System Overview (Light Theme)
refactor: rename group/groupName to application/applicationName
The execution-related "group" concept actually represents the
application name. Rename all Java fields, API parameters, and frontend
types from groupName→applicationName and group→application for clarity.
- Java records: ExecutionSummary, ExecutionDetail, ExecutionDocument,
ExecutionRecord, ProcessorRecord
- API params: SearchRequest.group→application, SearchController
@RequestParam group→application
- Services: IngestionService, DetailService, SearchIndexer, StatsStore
- Frontend: schema.d.ts, Dashboard, ExchangeDetail, RouteDetail,
executions query hooks
Database column names (group_name) and OpenSearch field names are
unchanged — only the API-facing Java/TS field names are renamed.
RBAC group references (groups table, GroupRepository, GroupsTab) are
a separate domain concept and are NOT affected by this change.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 21:21:38 +01:00
============================================================================
DESIGN NOTES
============
PURPOSE:
2026-04-15 15:28:42 +02:00
This page is the "Agents" view of the Cameleer monitoring dashboard. It
refactor: rename group/groupName to application/applicationName
The execution-related "group" concept actually represents the
application name. Rename all Java fields, API parameters, and frontend
types from groupName→applicationName and group→application for clarity.
- Java records: ExecutionSummary, ExecutionDetail, ExecutionDocument,
ExecutionRecord, ProcessorRecord
- API params: SearchRequest.group→application, SearchController
@RequestParam group→application
- Services: IngestionService, DetailService, SearchIndexer, StatsStore
- Frontend: schema.d.ts, Dashboard, ExchangeDetail, RouteDetail,
executions query hooks
Database column names (group_name) and OpenSearch field names are
unchanged — only the API-facing Java/TS field names are renamed.
RBAC group references (groups table, GroupRepository, GroupsTab) are
a separate domain concept and are NOT affected by this change.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 21:21:38 +01:00
shows the health of all Camel application instances (agents) reporting to
the server, grouped by application group.
INFORMATION HIERARCHY:
1. System Overview Strip — 5 summary cards (total agents, groups, routes,
uptime, ingestion rate) give instant situational awareness.
2. Agent Group Cards — 2-column grid of cards, one per application group.
Each card lists its instances with status dot, state badge, uptime,
throughput, error rate, and last heartbeat. The inventory-check card
has a single DEAD instance with a prominent alert.
3. Bottom Tabs — Timeline (lifecycle events) and Route Distribution
(which routes run on which agents).
DESIGN SYSTEM:
Exact match of mock-v2-light.html: warm parchment (#F5F2ED), amber-gold
(#C6820E), warm charcoal sidebar (#2C2520), DM Sans + JetBrains Mono,
identical card styles, shadows, border radius, status colors.
DATA STORY:
- 12 agents across 4 groups. 9 LIVE, 2 STALE, 1 DEAD.
- order-processing: 3 healthy instances, one recently restarted.
- payment-flow: 2 instances, one STALE (missed heartbeats 3m ago).
- shipment-notify: 2 healthy instances.
- inventory-check: 1 DEAD instance — single point of failure alert.
============================================================================
-->
<!DOCTYPE html>
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=1920" >
2026-04-15 15:28:42 +02:00
< title > Cameleer — Agent Health & System Overview< / title >
refactor: rename group/groupName to application/applicationName
The execution-related "group" concept actually represents the
application name. Rename all Java fields, API parameters, and frontend
types from groupName→applicationName and group→application for clarity.
- Java records: ExecutionSummary, ExecutionDetail, ExecutionDocument,
ExecutionRecord, ProcessorRecord
- API params: SearchRequest.group→application, SearchController
@RequestParam group→application
- Services: IngestionService, DetailService, SearchIndexer, StatsStore
- Frontend: schema.d.ts, Dashboard, ExchangeDetail, RouteDetail,
executions query hooks
Database column names (group_name) and OpenSearch field names are
unchanged — only the API-facing Java/TS field names are renamed.
RBAC group references (groups table, GroupRepository, GroupsTab) are
a separate domain concept and are NOT affected by this change.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 21:21:38 +01:00
< link href = "https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel = "stylesheet" >
< style >
/* ==========================================================================
RESET & FOUNDATIONS (identical to v2)
========================================================================== */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
/* Surface palette (warm parchment) */
--bg-body: #F5F2ED;
--bg-surface: #FFFFFF;
--bg-raised: #FAF8F5;
--bg-inset: #F0EDE8;
--bg-hover: #F5F0EA;
/* Sidebar (warm charcoal) */
--sidebar-bg: #2C2520;
--sidebar-hover: #3A322C;
--sidebar-active: #4A3F38;
--sidebar-text: #BFB5A8;
--sidebar-muted: #7A6F63;
/* Text */
--text-primary: #1A1612;
--text-secondary: #5C5347;
--text-muted: #9C9184;
--text-faint: #C4BAB0;
/* Borders */
--border: #E4DFD8;
--border-subtle: #EDE9E3;
/* Brand accent (amber-gold) */
--amber: #C6820E;
--amber-light: #F0D9A8;
--amber-bg: #FDF6E9;
--amber-deep: #8B5A06;
/* Status colors (warm) */
--success: #3D7C47;
--success-bg: #EFF7F0;
--success-border: #C2DFC6;
--warning: #C27516;
--warning-bg: #FEF5E7;
--warning-border: #F0D9A8;
--error: #C0392B;
--error-bg: #FDF0EE;
--error-border: #F0C4BE;
--running: #1A7F8E;
--running-bg: #E8F5F7;
--running-border: #B0DDE4;
/* Typography */
--font-body: 'DM Sans', system-ui, -apple-system, sans-serif;
--font-mono: 'JetBrains Mono', 'Fira Code', monospace;
/* Spacing & Radii */
--radius-sm: 5px;
--radius-md: 8px;
--radius-lg: 12px;
/* Shadows */
--shadow-sm: 0 1px 2px rgba(44, 37, 32, 0.06);
--shadow-md: 0 2px 8px rgba(44, 37, 32, 0.08);
--shadow-lg: 0 4px 16px rgba(44, 37, 32, 0.10);
--shadow-card: 0 1px 3px rgba(44, 37, 32, 0.04), 0 0 0 1px rgba(44, 37, 32, 0.04);
}
html { font-size: 14px; }
body {
font-family: var(--font-body);
background: var(--bg-body);
color: var(--text-primary);
line-height: 1.5;
min-height: 100vh;
-webkit-font-smoothing: antialiased;
}
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: var(--text-faint); }
/* ==========================================================================
ANIMATIONS
========================================================================== */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(6px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes pulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(61, 124, 71, 0.35); }
50% { box-shadow: 0 0 0 5px rgba(61, 124, 71, 0); }
}
@keyframes errorPulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(192, 57, 43, 0.2); }
50% { box-shadow: 0 0 0 4px rgba(192, 57, 43, 0); }
}
@keyframes warningPulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(194, 117, 22, 0.2); }
50% { box-shadow: 0 0 0 4px rgba(194, 117, 22, 0); }
}
.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; }
.delay-6 { animation-delay: 0.24s; }
.delay-7 { animation-delay: 0.28s; }
/* ==========================================================================
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-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 .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-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-divider {
height: 1px;
background: rgba(255,255,255,0.06);
margin: 6px 12px;
}
.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;
}
/* ==========================================================================
SYSTEM OVERVIEW STRIP (top 5 cards)
========================================================================== */
.health-strip {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 10px;
margin-bottom: 20px;
}
.stat-card {
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-md);
padding: 12px 16px;
box-shadow: var(--shadow-card);
position: relative;
overflow: hidden;
transition: box-shadow 0.15s;
cursor: pointer;
}
.stat-card:hover { box-shadow: var(--shadow-md); }
.stat-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
}
.stat-card.card-amber::before { background: linear-gradient(90deg, var(--amber), transparent); }
.stat-card.card-green::before { background: linear-gradient(90deg, var(--success), transparent); }
.stat-card.card-error::before { background: linear-gradient(90deg, var(--error), transparent); }
.stat-card.card-teal::before { background: linear-gradient(90deg, var(--running), transparent); }
.stat-card.card-warn::before { background: linear-gradient(90deg, var(--warning), transparent); }
.stat-card.has-warning { animation: warningPulse 2.5s ease-in-out infinite; }
.stat-label {
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.6px;
color: var(--text-muted);
margin-bottom: 3px;
}
.stat-value-row {
display: flex;
align-items: baseline;
gap: 6px;
}
.stat-value {
font-family: var(--font-mono);
font-size: 20px;
font-weight: 600;
line-height: 1.2;
}
.stat-value.val-amber { color: var(--amber); }
.stat-value.val-green { color: var(--success); }
.stat-value.val-error { color: var(--error); }
.stat-value.val-teal { color: var(--running); }
.stat-value.val-warn { color: var(--warning); }
.stat-unit {
font-size: 12px;
color: var(--text-muted);
}
.stat-detail {
font-size: 11px;
color: var(--text-muted);
margin-top: 2px;
}
.stat-detail strong { color: var(--text-secondary); }
.stat-breakdown {
display: flex;
gap: 8px;
margin-top: 4px;
font-size: 11px;
font-family: var(--font-mono);
}
.stat-breakdown .bp-live { color: var(--success); }
.stat-breakdown .bp-stale { color: var(--warning); }
.stat-breakdown .bp-dead { color: var(--error); }
.stat-breakdown .bp-dot {
display: inline-block;
width: 6px;
height: 6px;
border-radius: 50%;
margin-right: 3px;
vertical-align: middle;
}
.stat-breakdown .bp-dot-live { background: #5DB866; }
.stat-breakdown .bp-dot-stale { background: var(--warning); }
.stat-breakdown .bp-dot-dead { background: var(--text-muted); }
/* ==========================================================================
AGENT GROUP CARDS
========================================================================== */
.group-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
margin-bottom: 20px;
}
.group-card {
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-card);
overflow: hidden;
transition: box-shadow 0.15s;
}
.group-card:hover { box-shadow: var(--shadow-md); }
.group-card.group-alert {
border-color: var(--error-border);
animation: errorPulse 2.5s ease-in-out infinite;
}
.group-card-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
border-bottom: 1px solid var(--border-subtle);
background: var(--bg-raised);
}
.group-name {
font-size: 14px;
font-weight: 600;
font-family: var(--font-mono);
color: var(--text-primary);
}
.group-instance-count {
font-size: 11px;
font-family: var(--font-mono);
color: var(--text-muted);
background: var(--bg-inset);
padding: 2px 8px;
border-radius: 10px;
}
.group-meta {
display: flex;
gap: 16px;
padding: 8px 16px;
border-bottom: 1px solid var(--border-subtle);
font-size: 11px;
color: var(--text-muted);
background: var(--bg-surface);
}
.group-meta-item {
display: flex;
align-items: center;
gap: 4px;
}
.group-meta-item strong {
font-family: var(--font-mono);
color: var(--text-secondary);
font-weight: 600;
}
.group-instances {
padding: 0;
}
.instance-row {
display: grid;
grid-template-columns: 8px 1fr auto auto auto auto auto;
gap: 12px;
align-items: center;
padding: 8px 16px;
border-bottom: 1px solid var(--border-subtle);
font-size: 12px;
transition: background 0.1s;
}
.instance-row:last-child { border-bottom: none; }
.instance-row:hover { background: var(--bg-hover); }
.instance-dot {
width: 8px;
height: 8px;
border-radius: 50%;
flex-shrink: 0;
}
.dot-live { background: #5DB866; box-shadow: 0 0 6px rgba(93, 184, 102, 0.4); }
.dot-stale { background: var(--warning); box-shadow: 0 0 6px rgba(194, 117, 22, 0.3); }
.dot-dead { background: var(--text-muted); }
.instance-name {
font-family: var(--font-mono);
font-weight: 500;
font-size: 12px;
color: var(--text-primary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.instance-badge {
font-family: var(--font-mono);
font-size: 10px;
font-weight: 600;
padding: 2px 8px;
border-radius: 10px;
text-transform: uppercase;
letter-spacing: 0.3px;
white-space: nowrap;
}
.badge-live {
background: var(--success-bg);
color: var(--success);
border: 1px solid var(--success-border);
}
.badge-stale {
background: var(--warning-bg);
color: var(--warning);
border: 1px solid var(--warning-border);
}
.badge-dead {
background: var(--error-bg);
color: var(--error);
border: 1px solid var(--error-border);
}
.instance-uptime {
font-family: var(--font-mono);
font-size: 11px;
color: var(--text-muted);
white-space: nowrap;
}
.instance-throughput {
font-family: var(--font-mono);
font-size: 11px;
color: var(--text-secondary);
white-space: nowrap;
}
.instance-errors {
font-family: var(--font-mono);
font-size: 11px;
white-space: nowrap;
}
.instance-errors.low { color: var(--success); }
.instance-errors.med { color: var(--warning); }
.instance-errors.high { color: var(--error); }
.instance-heartbeat {
font-family: var(--font-mono);
font-size: 10px;
color: var(--text-muted);
white-space: nowrap;
text-align: right;
}
.instance-heartbeat.stale-hb { color: var(--warning); font-weight: 600; }
.instance-heartbeat.dead-hb { color: var(--error); font-weight: 600; }
.instance-note {
font-size: 10px;
color: var(--amber);
font-style: italic;
margin-left: 4px;
}
/* Alert banner inside group card */
.group-alert-banner {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 16px;
background: var(--error-bg);
border-top: 1px solid var(--error-border);
font-size: 11px;
color: var(--error);
font-weight: 500;
}
.group-alert-banner .alert-icon {
font-size: 14px;
flex-shrink: 0;
}
/* ==========================================================================
BOTTOM TABS (Timeline + Route Distribution)
========================================================================== */
.bottom-section {
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-card);
overflow: hidden;
}
.tab-bar {
display: flex;
border-bottom: 1px solid var(--border-subtle);
padding: 0 16px;
gap: 0;
}
.tab-item {
padding: 10px 16px;
font-size: 12px;
font-weight: 500;
color: var(--text-muted);
cursor: pointer;
border-bottom: 2px solid transparent;
transition: all 0.15s;
margin-bottom: -1px;
}
.tab-item:hover { color: var(--text-primary); }
.tab-item.active {
color: var(--amber);
border-bottom-color: var(--amber);
font-weight: 600;
}
.tab-content { padding: 0; }
.tab-panel { display: none; }
.tab-panel.active { display: block; }
/* Timeline */
.timeline-list { padding: 8px 0; }
.timeline-event {
display: flex;
align-items: flex-start;
gap: 12px;
padding: 8px 16px;
transition: background 0.1s;
}
.timeline-event:hover { background: var(--bg-hover); }
.timeline-icon {
width: 28px;
height: 28px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
flex-shrink: 0;
margin-top: 1px;
}
.timeline-icon.icon-start { background: var(--success-bg); color: var(--success); border: 1px solid var(--success-border); }
.timeline-icon.icon-dead { background: var(--error-bg); color: var(--error); border: 1px solid var(--error-border); }
.timeline-icon.icon-stale { background: var(--warning-bg); color: var(--warning); border: 1px solid var(--warning-border); }
.timeline-icon.icon-route { background: var(--running-bg); color: var(--running); border: 1px solid var(--running-border); }
.timeline-icon.icon-config { background: var(--amber-bg); color: var(--amber); border: 1px solid var(--amber-light); }
.timeline-body { flex: 1; min-width: 0; }
.timeline-text {
font-size: 12px;
color: var(--text-primary);
line-height: 1.4;
}
.timeline-text .tl-agent {
font-family: var(--font-mono);
font-weight: 600;
color: var(--text-primary);
}
.timeline-text .tl-group {
font-family: var(--font-mono);
color: var(--text-muted);
}
.timeline-text .tl-highlight-dead { color: var(--error); font-weight: 600; }
.timeline-text .tl-highlight-stale { color: var(--warning); font-weight: 600; }
.timeline-text .tl-highlight-start { color: var(--success); font-weight: 600; }
.timeline-time {
font-size: 10px;
font-family: var(--font-mono);
color: var(--text-muted);
margin-top: 1px;
}
/* Route Distribution Table */
.route-table-wrap { overflow-x: auto; }
.route-table {
width: 100%;
border-collapse: collapse;
font-size: 12px;
}
.route-table th {
padding: 8px 12px;
text-align: left;
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--text-muted);
background: var(--bg-raised);
border-bottom: 1px solid var(--border-subtle);
white-space: nowrap;
}
.route-table td {
padding: 6px 12px;
border-bottom: 1px solid var(--border-subtle);
font-family: var(--font-mono);
font-size: 11px;
color: var(--text-secondary);
}
.route-table tbody tr:hover { background: var(--bg-hover); }
.route-table tbody tr:last-child td { border-bottom: none; }
.route-table td.route-name {
font-weight: 500;
color: var(--text-primary);
}
.route-check {
display: inline-flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
border-radius: 50%;
font-size: 11px;
}
.route-check.present { background: var(--success-bg); color: var(--success); }
.route-check.absent { background: var(--bg-inset); color: var(--text-faint); }
.route-check.dead { background: var(--error-bg); color: var(--error); }
.route-status {
font-size: 10px;
font-weight: 600;
padding: 2px 8px;
border-radius: 10px;
white-space: nowrap;
}
.route-status.status-healthy { background: var(--success-bg); color: var(--success); border: 1px solid var(--success-border); }
.route-status.status-partial { background: var(--warning-bg); color: var(--warning); border: 1px solid var(--warning-border); }
.route-status.status-degraded { background: var(--error-bg); color: var(--error); border: 1px solid var(--error-border); }
/* ==========================================================================
RESPONSIVE INSTANCE ROW (adjust grid for content)
========================================================================== */
.instance-row {
grid-template-columns: 8px minmax(100px, 1.2fr) auto auto auto auto auto;
}
/* Column headers for instance list */
.instance-header {
display: grid;
grid-template-columns: 8px minmax(100px, 1.2fr) auto auto auto auto auto;
gap: 12px;
padding: 4px 16px;
font-size: 9px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--text-faint);
border-bottom: 1px solid var(--border-subtle);
}
< / style >
< / head >
< body >
< div class = "app" >
<!-- ====================================================================
SIDEBAR
==================================================================== -->
< aside class = "sidebar" >
< div class = "sidebar-logo" >
< svg width = "24" height = "26" viewBox = "940 300 900 1000" fill = "none" xmlns = "http://www.w3.org/2000/svg" >
< path style = "fill:#C6820E" d = "M 1385 . 2768 , 1205 . 0273 c -1 . 7727 , -1 . 0809 -3 . 4419 , -2 . 9917 -3 . 7093 , -4 . 2463 -0 . 2674 , -1 . 2545 -0 . 6851 , -23 . 881 -0 . 9282 , -50 . 281 -0 . 4544 , -49 . 3389 -1 . 4003 , -65 . 7355 -5 . 6645 , -98 . 1921 -10 . 4552 , -79 . 57719 -28 . 3441 , -125 . 96796 -62 . 5438 , -162 . 19311 -9 . 2529 , -9 . 80096 -19 . 9748 , -18 . 38696 -33 . 931 , -27 . 17162 -12 . 6119 , -7 . 93853 -24 . 8581 , -13 . 73088 -49 . 5 , -23 . 41316 -41 . 7081 , -16 . 38789 -67 . 8677 , -32 . 29827 -89 . 0588 , -54 . 16599 -34 . 7586 , -35 . 86827 -54 . 3653 , -83 . 30392 -59 . 0504 , -142 . 86402 -0 . 8357 , -10 . 6245 -0 . 92 , -22 . 5842 -0 . 2891 , -41 0 . 4899 , -14 . 3 0 . 8164 , -35 . 225 0 . 7255 , -46 . 5 -0 . 1983 , -24 . 59199 -2 . 0251 , -34 . 76973 -8 . 4617 , -47 . 14159 -6 . 2021 , -11 . 92135 -10 . 364 , -15 . 10789 -36 . 2345 , -27 . 74305 -25 . 4032 , -12 . 40694 -27 . 7701 , -13 . 30168 -35 . 4717 , -13 . 40961 -4 . 63013 , -0 . 0649 -7 . 47783 , 0 . 72754 -15 . 6593 , 4 . 35747 -9 . 86218 , 4 . 37563 -10 . 15161 , 4 . 43672 -21 , 4 . 43267 -10 . 86566 , -0 . 004 -11 . 08917 , -0 . 0517 -18 . 30169 , -3 . 90103 -17 . 43524 , -9 . 3052 -21 . 50075 , -23 . 5772 -13 . 55784 , -47 . 59486 2 . 73631 , -8 . 27402 10 . 00732 , -22 . 43593 14 . 69263 , -28 . 61719 5 . 4984 , -7 . 25394 10 . 89014 , -11 . 83885 20 . 83968 , -17 . 72117 15 . 06768 , -8 . 90827 29 . 60254 , -11 . 98884 63 . 90372 , -13 . 54403 l 16 . 5765 , -0 . 75156 14 . 9235 , -7 . 05735 c 8 . 2079 , -3 . 88154 17 . 1735 , -7 . 76831 19 . 9235 , -8 . 63727 7 . 7052 , -2 . 43474 21 . 059 , -4 . 67186 27 . 8605 , -4 . 66741 8 . 0518 , 0 . 005 22 . 643 , 2 . 41202 30 . 0139 , 4 . 95066 l 5 . 8744 , 2 . 02321 4 . 8856 , -4 . 09041 c 10 . 341 , -8 . 65797 18 . 6496 , -12 . 95738 28 . 96 , -14 . 98583 6 . 9966 , -1 . 37649 26 . 3532 , -0 . 64631 32 . 1116 , 1 . 21134 4 . 5531 , 1 . 46885 5 . 4951 , 3 . 7902 6 . 3689 , 15 . 69592 1 . 5167 , 20 . 66426 -5 . 0112 , 40 . 44987 -18 . 1772 , 55 . 09363 -4 . 3065 , 4 . 78983 -4 . 5016 , 5 . 25488 -3 . 4977 , 8 . 33516 4 . 5184 , 13 . 86447 5 . 3154 , 38 . 07517 2 . 1537 , 65 . 42564 -5 . 169 , 44 . 71349 -5 . 0411 , 70 . 7797 0 . 5404 , 110 . 15637 6 . 8135 , 48 . 06863 22 . 3335 , 73 . 51874 48 . 2051 , 79 . 04779 24 . 1748 , 5 . 16643 45 . 2921 , -5 . 78181 66 . 8353 , -34 . 65077 4 . 809 , -6 . 4442 11 . 1363 , -14 . 93006 14 . 0608 , -18 . 85747 7 . 3865 , -9 . 91943 25 . 6102 , -27 . 11708 35 . 952 , -33 . 92766 12 . 5235 , -8 . 24739 26 . 8808 , -14 . 74833 42 . 8527 , -19 . 40354 15 . 4108 , -4 . 49168 26 . 7091 , -9 . 74984 36 . 2432 , -16 . 86731 3 . 4338 , -2 . 56341 13 . 3338 , -12 . 03104 22 , -21 . 03916 26 . 2356 , -27 . 27067 44 . 5755 , -40 . 32368 66 . 9928 , -47 . 68062 19 . 1052 , -6 . 26998 35 . 4927 , -7 . 73718 50 . 8681 , -4 . 55427 37 . 0856 , 7 . 67726 63 . 5507 , 26 . 77589 100 . 2888 , 72 . 37362 16 . 6011 , 20 . 60463 29 . 9711 , 32 . 07977 51 . 6071 , 44 . 29313 39 . 3131 , 22 . 19195 50 . 1228 , 30 . 50985 68 . 8076 , 52 . 94655 5 . 4963 , 6 . 6 12 . 9187 , 14 . 91604 16 . 4941 , 18 . 4801 33 . 6796 , 33 . 57259 54 . 1965 , 72 . 51092 61 . 6587 , 117 . 0199 2 . 7315 , 16 . 29242 3 . 3374 , 26 . 62538 3 . 1861 , 54 . 33713 l -0 . 1465 , 26 . 83714 -2 . 7975 , 2 . 40572 c -3 . 9132 , 3 . 36522 -7 . 2806 , 3 . 99163 -11 . 2591 , 2 . 09442 -6 . 4731 , -3 . 08682 -6 . 5715 , -3 . 57833 -6 . 878 , -34 . 36929 -0 . 2908 , -29 . 2175 -2 . 0265 , -46 . 13705 -6 . 6479 , -64 . 80512 -3 . 2997 , -13 . 32891 -12 . 2529 , -34 . 61943 -17 . 0313 , -40 . 5 l -2 . 0314 , -2 . 5 0 . 5698 , 3 . 5 c 5 . 8574 , 35 . 97875 4 . 2855 , 72 . 40287 -5 . 3946 , 125 -8 . 4016 , 45 . 65054 -4 . 3665 , 69 . 39588 20 . 7318 , 122 11 . 4175 , 23 . 93009 12 . 5452 , 27 . 25907 12 . 4212 , 36 . 6684 -0 . 1018 , 7 . 7227 -2 . 5346 , 19 . 5162 -13 . 1674 , 63 . 8316 -1 . 0558 , 4 . 4 -3 . 5336 , 14 . 975 -5 . 5063 , 23 . 5 -1 . 9727 , 8 . 525 -4 . 2259 , 18 . 2 -5 . 0072 , 21 . 5 -0 . 7812 , 3 . 3 -3 . 4495 , 15 . 225 -5 . 9296 , 26 . 5 -9 . 1636 , 41 . 6596 -13 . 4372 , 59 . 8787 -14 . 5204 , 61 . 9027 -2 . 4994 , 4 . 6702 -5 . 2312 , 5 . 0973 -32 . 6024 , 5 . 0973 H 1765 . 8 l -3 . 4 , -3 . 4 c -2 . 3518 , -2 . 3518 -3 . 4 , -4 . 3226 -3 . 4 , -6 . 3925 0 , -1 . 6458 2 . 2347 , -12 . 1533 4 . 966 , -23 . 35 8 . 6906 , -35 . 6259 11 . 6969 , -54 . 778 12 . 6921 , -80 . 8575 1 . 3475 , -35 . 3073 -4 . 6406 , -62 . 7687 -18 . 7825 , -86 . 137 -7 . 6672 , -12 . 66954 -11 . 9163 , -17 . 84148 -29 . 8756 , -36 . 36415 -22 . 9362 , -23 . 65574 -34 . 6222 , -39 . 72583 -47 . 9268 , -65 . 90697 -5 . 7294 , -11 . 27448 -13 . 6061 , -31 . 99995 -15 . 5051 , -40 . 79778 -0 . 3212 , -1 . 48824 -1 . 0176 , -3 . 84619 -1 . 5475 , -5 . 2399 l -0 . 9634 , -2 . 534 -11 . 7786 , 7 . 15145 c -38 . 634 , 23 . 45687 -74 . 5513 , 34 . 71091 -124 . 1895 , 38 . 91257 -14 . 5402 , 1 . 23075 -58 . 2359 , 0 . 66344 -71 . 9142 , -0 . 93369 -5 . 4419 , -0 . 63542 -6 . 4542 , -0 . 49723 -7 . 25 , 0 . 98972 -0 . 5545 , 1 . 03621 -0 . 9249 , 60 . 63835 -0 . 9249 , 148 . 84365 0 , 161 . 6262 0 . 4025 , 151 . 2052 -5 . 9673 , 154 . 4992 -2 . 3865 , 1 . 2341 -7 . 4633 , 1 . 5162 -27 . 233 , 1 . 5132 -22 . 3926 , -0 -24 . 5527 , -0 . 158 -27 . 5229 , -1 . 969 z M 1424 , 1045 . 6452 c 0 , -87 . 21053 0 . 3878 , -144 . 17994 1 . 0365 , -152 . 24998 1 . 8115 , -22 . 53829 7 . 2373 , -44 . 8067 16 . 1142 , -66 . 13567 20 . 7842 , -49 . 93942 66 . 8961 , -95 . 01414 129 . 8493 , -126 . 92865 14 . 9469 , -7 . 57742 29 . 283
< / svg >
< div >
< span class = "brand" > cameleer< / span > < span class = "version" > v3< / span >
< / 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" > Executions< / 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" > Agents< / div > < / div >
< span class = "item-count" > 12< / span >
< / div >
< div class = "sidebar-item" >
< span style = "font-size: 13px; width: 18px; text-align: center;" > ◯ < / span >
< div class = "item-info" > < div class = "item-name" > Routes< / div > < / div >
< span class = "item-count" > 24< / span >
< / div >
< div class = "sidebar-item" >
< span style = "font-size: 13px; width: 18px; text-align: center;" > ⚙ < / span >
< div class = "item-info" > < div class = "item-name" > Diagrams< / div > < / div >
< / div >
< div class = "sidebar-divider" > < / div >
< div class = "sidebar-section" > Groups< / div >
< div class = "sidebar-item" >
< span class = "health health-live" > < / span >
< div class = "item-info" >
< div class = "item-name" > order-processing< / div >
< div class = "item-meta" > 3 agents< / div >
< / div >
< / div >
< div class = "sidebar-item" >
< span class = "health health-stale" > < / span >
< div class = "item-info" >
< div class = "item-name" > payment-flow< / div >
< div class = "item-meta" > 2 agents< / div >
< / div >
< / div >
< div class = "sidebar-item" >
< span class = "health health-live" > < / span >
< div class = "item-info" >
< div class = "item-name" > shipment-notify< / div >
< div class = "item-meta" > 2 agents< / div >
< / div >
< / div >
< div class = "sidebar-item" >
< span class = "health health-dead" > < / span >
< div class = "item-info" >
< div class = "item-name" > inventory-check< / div >
< div class = "item-meta" > 1 agent< / div >
< / div >
< / div >
< div class = "sidebar-divider" > < / div >
< div class = "sidebar-section" > Quick Filters< / div >
< div class = "sidebar-item" >
< span class = "health health-stale" > < / span >
< div class = "item-info" > < div class = "item-name" > Stale Agents< / div > < / div >
< span class = "item-count" > 2< / span >
< / div >
< div class = "sidebar-item" >
< span class = "health health-dead" > < / span >
< div class = "item-info" > < div class = "item-name" > Dead Agents< / div > < / div >
< span class = "item-count" > 1< / span >
< / 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 > System< / span >
< span class = "crumb-sep" > ▸ < / span >
< span class = "crumb-active" > Agents< / span >
< / div >
< div class = "topbar-right" >
< span class = "topbar-env" > Production< / span >
< span class = "topbar-shift" > Since 06:00< / span >
< div class = "topbar-user" >
< span > ops-admin< / span >
< div class = "topbar-avatar" > OA< / div >
< / div >
< / div >
< / div >
<!-- Scrollable content -->
< div class = "content" >
<!-- ================================================================
SYSTEM OVERVIEW STRIP
================================================================ -->
< div class = "health-strip animate-in" >
< div class = "stat-card card-amber has-warning" >
< div class = "stat-label" > Total Agents< / div >
< div class = "stat-value-row" >
< span class = "stat-value val-amber" > 12< / span >
< span class = "stat-unit" > reporting< / span >
< / div >
< div class = "stat-breakdown" >
< span class = "bp-live" > < span class = "bp-dot bp-dot-live" > < / span > 9< / span >
< span class = "bp-stale" > < span class = "bp-dot bp-dot-stale" > < / span > 2< / span >
< span class = "bp-dead" > < span class = "bp-dot bp-dot-dead" > < / span > 1< / span >
< / div >
< / div >
< div class = "stat-card card-teal" >
< div class = "stat-label" > Groups< / div >
< div class = "stat-value-row" >
< span class = "stat-value val-teal" > 4< / span >
< span class = "stat-unit" > applications< / span >
< / div >
< div class = "stat-detail" > < strong > 3< / strong > healthy · < strong > 1< / strong > degraded< / div >
< / div >
< div class = "stat-card card-green" >
< div class = "stat-label" > Active Routes< / div >
< div class = "stat-value-row" >
< span class = "stat-value val-green" > 24< / span >
< span class = "stat-unit" > routes< / span >
< / div >
< div class = "stat-detail" > across all agents< / div >
< / div >
< div class = "stat-card card-green" >
< div class = "stat-label" > System Uptime< / div >
< div class = "stat-value-row" >
< span class = "stat-value val-green" > 14d< / span >
< span class = "stat-unit" > 3h 22m< / span >
< / div >
< div class = "stat-detail" > since Mar 3 02:38 UTC< / div >
< / div >
< div class = "stat-card card-amber" >
< div class = "stat-label" > Data Ingestion< / div >
< div class = "stat-value-row" >
< span class = "stat-value val-amber" > 847< / span >
< span class = "stat-unit" > msg/min< / span >
< / div >
< div class = "stat-detail" > ▲ 4.2% vs yesterday< / div >
< / div >
< / div >
<!-- ================================================================
AGENT GROUP CARDS (2-column grid)
================================================================ -->
< div class = "group-grid" >
<!-- order - processing — 3 healthy instances -->
< div class = "group-card animate-in delay-1" >
< div class = "group-card-header" >
< span class = "group-name" > order-processing< / span >
< span class = "group-instance-count" > 3 instances< / span >
< / div >
< div class = "group-meta" >
< div class = "group-meta-item" > Routes: < strong > 8< / strong > < / div >
< div class = "group-meta-item" > Avg Throughput: < strong > 248/min< / strong > < / div >
< div class = "group-meta-item" > Errors: < strong > 0.3%< / strong > < / div >
< / div >
< div class = "instance-header" >
< span > < / span >
< span > Agent ID< / span >
< span > State< / span >
< span > Uptime< / span >
< span > Throughput< / span >
< span > Err %< / span >
< span style = "text-align: right;" > Heartbeat< / span >
< / div >
< div class = "group-instances" >
< div class = "instance-row" >
< span class = "instance-dot dot-live" > < / span >
< span class = "instance-name" > prod-node-01< / span >
< span class = "instance-badge badge-live" > Live< / span >
< span class = "instance-uptime" > ↑ 14d 3h< / span >
< span class = "instance-throughput" > 248/min< / span >
< span class = "instance-errors low" > 0.2%< / span >
< span class = "instance-heartbeat" > 5s ago< / span >
< / div >
< div class = "instance-row" >
< span class = "instance-dot dot-live" > < / span >
< span class = "instance-name" > prod-node-02< / span >
< span class = "instance-badge badge-live" > Live< / span >
< span class = "instance-uptime" > ↑ 14d 3h< / span >
< span class = "instance-throughput" > 251/min< / span >
< span class = "instance-errors low" > 0.4%< / span >
< span class = "instance-heartbeat" > 3s ago< / span >
< / div >
< div class = "instance-row" >
< span class = "instance-dot dot-live" > < / span >
< span class = "instance-name" > prod-node-03< / span >
< span class = "instance-badge badge-live" > Live< / span >
< span class = "instance-uptime" > ↑ 2h 15m< span class = "instance-note" > restarted< / span > < / span >
< span class = "instance-throughput" > 245/min< / span >
< span class = "instance-errors low" > 0.3%< / span >
< span class = "instance-heartbeat" > 8s ago< / span >
< / div >
< / div >
< / div >
<!-- payment - flow — 2 instances, one STALE -->
< div class = "group-card animate-in delay-2" >
< div class = "group-card-header" >
< span class = "group-name" > payment-flow< / span >
< span class = "group-instance-count" > 2 instances< / span >
< / div >
< div class = "group-meta" >
< div class = "group-meta-item" > Routes: < strong > 6< / strong > < / div >
< div class = "group-meta-item" > Avg Throughput: < strong > 182/min< / strong > < / div >
< div class = "group-meta-item" > Errors: < strong > 0.8%< / strong > < / div >
< / div >
< div class = "instance-header" >
< span > < / span >
< span > Agent ID< / span >
< span > State< / span >
< span > Uptime< / span >
< span > Throughput< / span >
< span > Err %< / span >
< span style = "text-align: right;" > Heartbeat< / span >
< / div >
< div class = "group-instances" >
< div class = "instance-row" >
< span class = "instance-dot dot-live" > < / span >
< span class = "instance-name" > prod-pay-01< / span >
< span class = "instance-badge badge-live" > Live< / span >
< span class = "instance-uptime" > ↑ 14d 3h< / span >
< span class = "instance-throughput" > 194/min< / span >
< span class = "instance-errors low" > 0.5%< / span >
< span class = "instance-heartbeat" > 2s ago< / span >
< / div >
< div class = "instance-row" >
< span class = "instance-dot dot-stale" > < / span >
< span class = "instance-name" > prod-pay-02< / span >
< span class = "instance-badge badge-stale" > Stale< / span >
< span class = "instance-uptime" > ↑ 14d 3h< / span >
< span class = "instance-throughput" > 170/min< / span >
< span class = "instance-errors med" > 1.1%< / span >
< span class = "instance-heartbeat stale-hb" > 3m ago< / span >
< / div >
< / div >
< / div >
<!-- shipment - notify — 2 healthy instances -->
< div class = "group-card animate-in delay-3" >
< div class = "group-card-header" >
< span class = "group-name" > shipment-notify< / span >
< span class = "group-instance-count" > 2 instances< / span >
< / div >
< div class = "group-meta" >
< div class = "group-meta-item" > Routes: < strong > 4< / strong > < / div >
< div class = "group-meta-item" > Avg Throughput: < strong > 96/min< / strong > < / div >
< div class = "group-meta-item" > Errors: < strong > 0.1%< / strong > < / div >
< / div >
< div class = "instance-header" >
< span > < / span >
< span > Agent ID< / span >
< span > State< / span >
< span > Uptime< / span >
< span > Throughput< / span >
< span > Err %< / span >
< span style = "text-align: right;" > Heartbeat< / span >
< / div >
< div class = "group-instances" >
< div class = "instance-row" >
< span class = "instance-dot dot-live" > < / span >
< span class = "instance-name" > prod-ship-01< / span >
< span class = "instance-badge badge-live" > Live< / span >
< span class = "instance-uptime" > ↑ 14d 3h< / span >
< span class = "instance-throughput" > 98/min< / span >
< span class = "instance-errors low" > 0.1%< / span >
< span class = "instance-heartbeat" > 6s ago< / span >
< / div >
< div class = "instance-row" >
< span class = "instance-dot dot-live" > < / span >
< span class = "instance-name" > prod-ship-02< / span >
< span class = "instance-badge badge-live" > Live< / span >
< span class = "instance-uptime" > ↑ 14d 3h< / span >
< span class = "instance-throughput" > 94/min< / span >
< span class = "instance-errors low" > 0.1%< / span >
< span class = "instance-heartbeat" > 4s ago< / span >
< / div >
< / div >
< / div >
<!-- inventory - check — 1 DEAD instance, SPOF alert -->
< div class = "group-card group-alert animate-in delay-4" >
< div class = "group-card-header" style = "background: var(--error-bg);" >
< span class = "group-name" style = "color: var(--error);" > inventory-check< / span >
< span class = "group-instance-count" style = "background: var(--error-bg); color: var(--error); border: 1px solid var(--error-border);" > 1 instance< / span >
< / div >
< div class = "group-meta" style = "background: var(--error-bg); border-bottom-color: var(--error-border);" >
< div class = "group-meta-item" > Routes: < strong > 6< / strong > < / div >
< div class = "group-meta-item" > Avg Throughput: < strong style = "color: var(--error);" > 0/min< / strong > < / div >
< div class = "group-meta-item" > Errors: < strong style = "color: var(--error);" > — < / strong > < / div >
< / div >
< div class = "instance-header" >
< span > < / span >
< span > Agent ID< / span >
< span > State< / span >
< span > Uptime< / span >
< span > Throughput< / span >
< span > Err %< / span >
< span style = "text-align: right;" > Heartbeat< / span >
< / div >
< div class = "group-instances" >
< div class = "instance-row" >
< span class = "instance-dot dot-dead" > < / span >
< span class = "instance-name" > staging-01< / span >
< span class = "instance-badge badge-dead" > Dead< / span >
< span class = "instance-uptime" style = "color: var(--text-faint);" > — < / span >
< span class = "instance-throughput" style = "color: var(--text-faint);" > 0/min< / span >
< span class = "instance-errors" style = "color: var(--text-faint);" > — < / span >
< span class = "instance-heartbeat dead-hb" > 45m ago< / span >
< / div >
< / div >
< div class = "group-alert-banner" >
< span class = "alert-icon" > ⚠ < / span >
< span > Single point of failure — this group has only 1 instance and it is < strong > DEAD< / strong > . Last heartbeat 45 minutes ago. Inventory checking is fully offline.< / span >
< / div >
< / div >
< / div >
<!-- ================================================================
BOTTOM TABS: Timeline | Route Distribution
================================================================ -->
< div class = "bottom-section animate-in delay-5" >
< div class = "tab-bar" >
< div class = "tab-item active" onclick = "switchTab('timeline')" > Timeline< / div >
< div class = "tab-item" onclick = "switchTab('routes')" > Route Distribution< / div >
< / div >
< div class = "tab-content" >
<!-- Timeline Tab -->
< div class = "tab-panel active" id = "tab-timeline" >
< div class = "timeline-list" >
< div class = "timeline-event" >
< div class = "timeline-icon icon-stale" > ⚠ < / div >
< div class = "timeline-body" >
< div class = "timeline-text" >
< span class = "tl-agent" > prod-pay-02< / span >
< span class = "tl-group" > (payment-flow)< / span >
went < span class = "tl-highlight-stale" > STALE< / span > — missed 3 consecutive heartbeats
< / div >
< div class = "timeline-time" > 3 minutes ago · 17 Mar 2026, 14:57 UTC< / div >
< / div >
< / div >
< div class = "timeline-event" >
< div class = "timeline-icon icon-dead" > ✖ < / div >
< div class = "timeline-body" >
< div class = "timeline-text" >
< span class = "tl-agent" > staging-01< / span >
< span class = "tl-group" > (inventory-check)< / span >
went < span class = "tl-highlight-dead" > DEAD< / span > — no heartbeat for 15 minutes
< / div >
< div class = "timeline-time" > 45 minutes ago · 17 Mar 2026, 14:15 UTC< / div >
< / div >
< / div >
< div class = "timeline-event" >
< div class = "timeline-icon icon-route" > ↻ < / div >
< div class = "timeline-body" >
< div class = "timeline-text" >
< span class = "tl-agent" > prod-node-01< / span >
< span class = "tl-group" > (order-processing)< / span >
route < strong > order-retry< / strong > restarted after error threshold reached
< / div >
< div class = "timeline-time" > 1 hour ago · 17 Mar 2026, 14:00 UTC< / div >
< / div >
< / div >
< div class = "timeline-event" >
< div class = "timeline-icon icon-start" > ▶ < / div >
< div class = "timeline-body" >
< div class = "timeline-text" >
< span class = "tl-agent" > prod-node-03< / span >
< span class = "tl-group" > (order-processing)< / span >
< span class = "tl-highlight-start" > started< / span > — registered with server, 8 routes loaded
< / div >
< div class = "timeline-time" > 2 hours 15 minutes ago · 17 Mar 2026, 12:45 UTC< / div >
< / div >
< / div >
< div class = "timeline-event" >
< div class = "timeline-icon icon-config" > ⚙ < / div >
< div class = "timeline-body" >
< div class = "timeline-text" >
Config push to < span class = "tl-agent" > prod-pay-01< / span >
< span class = "tl-group" > (payment-flow)< / span >
— updated log level to DEBUG for route < strong > payment-charge< / strong >
< / div >
< div class = "timeline-time" > 3 hours ago · 17 Mar 2026, 12:00 UTC< / div >
< / div >
< / div >
< div class = "timeline-event" >
< div class = "timeline-icon icon-stale" > ⚠ < / div >
< div class = "timeline-body" >
< div class = "timeline-text" >
< span class = "tl-agent" > staging-01< / span >
< span class = "tl-group" > (inventory-check)< / span >
went < span class = "tl-highlight-stale" > STALE< / span > — first missed heartbeat
< / div >
< div class = "timeline-time" > 1 hour ago · 17 Mar 2026, 14:00 UTC< / div >
< / div >
< / div >
< div class = "timeline-event" >
< div class = "timeline-icon icon-start" > ▶ < / div >
< div class = "timeline-body" >
< div class = "timeline-text" >
< span class = "tl-agent" > prod-ship-01< / span >
< span class = "tl-group" > (shipment-notify)< / span >
deployed version < strong > v3.2.1< / strong > — rolling update completed
< / div >
< div class = "timeline-time" > 6 hours ago · 17 Mar 2026, 09:00 UTC< / div >
< / div >
< / div >
< / div >
< / div >
<!-- Route Distribution Tab -->
< div class = "tab-panel" id = "tab-routes" >
< div class = "route-table-wrap" >
< table class = "route-table" >
< thead >
< tr >
< th > Route< / th >
< th > Group< / th >
< th > prod-node-01< / th >
< th > prod-node-02< / th >
< th > prod-node-03< / th >
< th > prod-pay-01< / th >
< th > prod-pay-02< / th >
< th > prod-ship-01< / th >
< th > prod-ship-02< / th >
< th > staging-01< / th >
< th > Coverage< / th >
< / tr >
< / thead >
< tbody >
< tr >
< td class = "route-name" > order-intake< / td >
< td > order-processing< / td >
< td > < span class = "route-check present" > ✓ < / span > < / td >
< td > < span class = "route-check present" > ✓ < / span > < / td >
< td > < span class = "route-check present" > ✓ < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-status status-healthy" > 3/3< / span > < / td >
< / tr >
< tr >
< td class = "route-name" > order-enrichment< / td >
< td > order-processing< / td >
< td > < span class = "route-check present" > ✓ < / span > < / td >
< td > < span class = "route-check present" > ✓ < / span > < / td >
< td > < span class = "route-check present" > ✓ < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-status status-healthy" > 3/3< / span > < / td >
< / tr >
< tr >
< td class = "route-name" > order-retry< / td >
< td > order-processing< / td >
< td > < span class = "route-check present" > ✓ < / span > < / td >
< td > < span class = "route-check present" > ✓ < / span > < / td >
< td > < span class = "route-check present" > ✓ < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-status status-healthy" > 3/3< / span > < / td >
< / tr >
< tr >
< td class = "route-name" > payment-charge< / td >
< td > payment-flow< / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check present" > ✓ < / span > < / td >
< td > < span class = "route-check present" > ✓ < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-status status-partial" > 1/2 live< / span > < / td >
< / tr >
< tr >
< td class = "route-name" > payment-refund< / td >
< td > payment-flow< / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check present" > ✓ < / span > < / td >
< td > < span class = "route-check present" > ✓ < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-status status-partial" > 1/2 live< / span > < / td >
< / tr >
< tr >
< td class = "route-name" > ship-dispatch< / td >
< td > shipment-notify< / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check present" > ✓ < / span > < / td >
< td > < span class = "route-check present" > ✓ < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-status status-healthy" > 2/2< / span > < / td >
< / tr >
< tr >
< td class = "route-name" > ship-track-update< / td >
< td > shipment-notify< / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check present" > ✓ < / span > < / td >
< td > < span class = "route-check present" > ✓ < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-status status-healthy" > 2/2< / span > < / td >
< / tr >
< tr >
< td class = "route-name" > inv-stock-check< / td >
< td > inventory-check< / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check dead" > ✖ < / span > < / td >
< td > < span class = "route-status status-degraded" > 0/1 dead< / span > < / td >
< / tr >
< tr >
< td class = "route-name" > inv-reorder< / td >
< td > inventory-check< / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check absent" > — < / span > < / td >
< td > < span class = "route-check dead" > ✖ < / span > < / td >
< td > < span class = "route-status status-degraded" > 0/1 dead< / span > < / td >
< / tr >
< / tbody >
< / table >
< / div >
< / div >
< / div >
< / div >
< / div > <!-- /content -->
< / div > <!-- /main -->
< / div > <!-- /app -->
< script >
function switchTab(tab) {
// Update tab bar
document.querySelectorAll('.tab-item').forEach(function(el) {
el.classList.remove('active');
});
event.target.classList.add('active');
// Update tab panels
document.querySelectorAll('.tab-panel').forEach(function(el) {
el.classList.remove('active');
});
document.getElementById('tab-' + tab).classList.add('active');
}
< / script >
< / body >
< / html >