1491 lines
59 KiB
HTML
1491 lines
59 KiB
HTML
|
|
<!--
|
||
|
|
============================================================================
|
||
|
|
CAMELEER3 v3 — Agent Health & System Overview (Light Theme)
|
||
|
|
============================================================================
|
||
|
|
|
||
|
|
DESIGN NOTES
|
||
|
|
============
|
||
|
|
|
||
|
|
PURPOSE:
|
||
|
|
This page is the "Agents" view of the Cameleer3 monitoring dashboard. It
|
||
|
|
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">
|
||
|
|
<title>Cameleer3 — Agent Health & System Overview</title>
|
||
|
|
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||
|
|
<style>
|
||
|
|
/* ==========================================================================
|
||
|
|
RESET & FOUNDATIONS (identical to v2)
|
||
|
|
========================================================================== */
|
||
|
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||
|
|
|
||
|
|
:root {
|
||
|
|
/* Surface palette (warm parchment) */
|
||
|
|
--bg-body: #F5F2ED;
|
||
|
|
--bg-surface: #FFFFFF;
|
||
|
|
--bg-raised: #FAF8F5;
|
||
|
|
--bg-inset: #F0EDE8;
|
||
|
|
--bg-hover: #F5F0EA;
|
||
|
|
|
||
|
|
/* Sidebar (warm charcoal) */
|
||
|
|
--sidebar-bg: #2C2520;
|
||
|
|
--sidebar-hover: #3A322C;
|
||
|
|
--sidebar-active: #4A3F38;
|
||
|
|
--sidebar-text: #BFB5A8;
|
||
|
|
--sidebar-muted: #7A6F63;
|
||
|
|
|
||
|
|
/* Text */
|
||
|
|
--text-primary: #1A1612;
|
||
|
|
--text-secondary: #5C5347;
|
||
|
|
--text-muted: #9C9184;
|
||
|
|
--text-faint: #C4BAB0;
|
||
|
|
|
||
|
|
/* Borders */
|
||
|
|
--border: #E4DFD8;
|
||
|
|
--border-subtle: #EDE9E3;
|
||
|
|
|
||
|
|
/* Brand accent (amber-gold) */
|
||
|
|
--amber: #C6820E;
|
||
|
|
--amber-light: #F0D9A8;
|
||
|
|
--amber-bg: #FDF6E9;
|
||
|
|
--amber-deep: #8B5A06;
|
||
|
|
|
||
|
|
/* Status colors (warm) */
|
||
|
|
--success: #3D7C47;
|
||
|
|
--success-bg: #EFF7F0;
|
||
|
|
--success-border: #C2DFC6;
|
||
|
|
--warning: #C27516;
|
||
|
|
--warning-bg: #FEF5E7;
|
||
|
|
--warning-border: #F0D9A8;
|
||
|
|
--error: #C0392B;
|
||
|
|
--error-bg: #FDF0EE;
|
||
|
|
--error-border: #F0C4BE;
|
||
|
|
--running: #1A7F8E;
|
||
|
|
--running-bg: #E8F5F7;
|
||
|
|
--running-border: #B0DDE4;
|
||
|
|
|
||
|
|
/* Typography */
|
||
|
|
--font-body: 'DM Sans', system-ui, -apple-system, sans-serif;
|
||
|
|
--font-mono: 'JetBrains Mono', 'Fira Code', monospace;
|
||
|
|
|
||
|
|
/* Spacing & Radii */
|
||
|
|
--radius-sm: 5px;
|
||
|
|
--radius-md: 8px;
|
||
|
|
--radius-lg: 12px;
|
||
|
|
|
||
|
|
/* Shadows */
|
||
|
|
--shadow-sm: 0 1px 2px rgba(44, 37, 32, 0.06);
|
||
|
|
--shadow-md: 0 2px 8px rgba(44, 37, 32, 0.08);
|
||
|
|
--shadow-lg: 0 4px 16px rgba(44, 37, 32, 0.10);
|
||
|
|
--shadow-card: 0 1px 3px rgba(44, 37, 32, 0.04), 0 0 0 1px rgba(44, 37, 32, 0.04);
|
||
|
|
}
|
||
|
|
|
||
|
|
html { font-size: 14px; }
|
||
|
|
body {
|
||
|
|
font-family: var(--font-body);
|
||
|
|
background: var(--bg-body);
|
||
|
|
color: var(--text-primary);
|
||
|
|
line-height: 1.5;
|
||
|
|
min-height: 100vh;
|
||
|
|
-webkit-font-smoothing: antialiased;
|
||
|
|
}
|
||
|
|
|
||
|
|
::-webkit-scrollbar { width: 6px; height: 6px; }
|
||
|
|
::-webkit-scrollbar-track { background: transparent; }
|
||
|
|
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
|
||
|
|
::-webkit-scrollbar-thumb:hover { background: var(--text-faint); }
|
||
|
|
|
||
|
|
/* ==========================================================================
|
||
|
|
ANIMATIONS
|
||
|
|
========================================================================== */
|
||
|
|
@keyframes fadeIn {
|
||
|
|
from { opacity: 0; transform: translateY(6px); }
|
||
|
|
to { opacity: 1; transform: translateY(0); }
|
||
|
|
}
|
||
|
|
@keyframes pulse {
|
||
|
|
0%, 100% { box-shadow: 0 0 0 0 rgba(61, 124, 71, 0.35); }
|
||
|
|
50% { box-shadow: 0 0 0 5px rgba(61, 124, 71, 0); }
|
||
|
|
}
|
||
|
|
@keyframes 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>
|