deb04c9315
Sprint 0 completo del producto VMS-Sailor (Vessel Management System integrado para buques 30-40m). Brief de referencia en VMS_Sailor_v2_Parte_*.md (intacto). Core (vmssailor.core, 95.17% coverage, 99 tests verde): - ShipCoord: sistema naval x_pp/y_cl/z_bl frozen - Vessel, Deck, Bulkhead - Equipment, EquipmentModel, Sensor, EquipmentSpec - Tag, AlarmConfig, TagBinding, Scaling - CardInstance, Bus, Topology con validacion 21 puntos I/O AR-NMEA-IO-v1.0 - Alarm, PermissiveRule, Condition - Project agregado raiz con validacion cross-entity - Persistencia portable .vmsproj (SQLite) con roundtrip verificable Biblioteca curada seed (vmssailor.library): - systems_catalog.json completo (catalogo maestro Parte 1 sec 7) - 2 vessels: Sunseeker 76, Ferretti 850 - 2 motores: MTU 12V 2000 M96, Volvo D13-900 - 1 genset: Northern Lights M65C13 - yacht_motor_planeo.yaml (reglas heuristicas) - TODO marcado data_source=seed_estimate - requiere validacion datasheets Tools: - vms-validate-library: CLI valida biblioteca completa - vms-generate-test-project: CLI demo + verificacion roundtrip persistencia Design System + 8 mockups HTML estaticos: - docs/design_system.md (paleta Deep Ocean, gradientes, typography, motion) - docs/brand/ (logo + variantes SVG) - docs/mockups/splash, studio_main, runtime_overview, runtime_mimic_fuel (P&ID animado), runtime_alarms, runtime_trim (panel estrella con horizonte artificial), mobile_overview, mobile_trim - docs/mockups/index.html (galeria) Firmware (Sprint 12+ implementacion): - firmware/ar_nmea_io_v1/src/config/pinout.h con macros GPIO Decisiones autonomas documentadas en docs/decisions_sprint0.md. Stack: Python 3.11 + uv + Pydantic v2 + SQLite stdlib + hatchling + pytest 9 + ruff + mypy. Sin PySide6, FastAPI, Flutter ni firmware funcional (entran en sprints siguientes). Criterio de aceptacion Sprint 0: cumplido. - uv sync: OK - pytest: 99/99 verde - cov vmssailor.core: 95.17% (objetivo >=80%) - ruff: clean - vms-validate-library: OK - vms-generate-test-project: INTEGRIDAD OK Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
491 lines
18 KiB
HTML
491 lines
18 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="es">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>VMS-Sailor · Panel de alarmas</title>
|
|
<link rel="icon" type="image/svg+xml" href="../brand/favicon.svg">
|
|
<link rel="stylesheet" href="_tokens.css">
|
|
<style>
|
|
body { overflow: hidden; }
|
|
.rt {
|
|
display: grid;
|
|
grid-template-rows: 56px 1fr 32px;
|
|
grid-template-columns: 240px 1fr;
|
|
grid-template-areas:
|
|
"topbar topbar"
|
|
"sidebar main"
|
|
"ticker ticker";
|
|
height: 100vh;
|
|
}
|
|
.topbar {
|
|
grid-area: topbar;
|
|
display: flex; align-items: center; gap: var(--s-5);
|
|
padding: 0 var(--s-5);
|
|
background: var(--c-midnight);
|
|
border-bottom: 1px solid var(--c-steel);
|
|
}
|
|
.breadcrumb { display: flex; align-items: center; gap: var(--s-3); font-size: 13px; color: var(--c-fog); }
|
|
.breadcrumb img { height: 28px; }
|
|
.breadcrumb strong { color: var(--c-foam); font-family: var(--f-display); font-size: 16px; }
|
|
.breadcrumb .sep { opacity: 0.5; }
|
|
.breadcrumb .system { color: var(--c-cyan); font-weight: 600; }
|
|
.top-spacer { flex: 1; }
|
|
.stats-strip {
|
|
display: flex; gap: var(--s-5); align-items: center;
|
|
font-family: var(--f-mono);
|
|
font-size: 12px;
|
|
}
|
|
.stats-strip .item { display: flex; gap: 6px; align-items: baseline; }
|
|
.stats-strip .num { font-size: 18px; font-weight: 600; color: var(--c-foam); }
|
|
.stats-strip .num.emergency { color: var(--c-emergency); }
|
|
.stats-strip .num.high { color: var(--c-high); }
|
|
.stats-strip .num.low { color: var(--c-warn); }
|
|
.stats-strip .num.info { color: var(--c-info); }
|
|
.stats-strip .lbl { color: var(--c-fog); text-transform: uppercase; letter-spacing: 1.5px; font-size: 10px; font-weight: 700;}
|
|
|
|
.sidebar {
|
|
grid-area: sidebar;
|
|
background: var(--c-midnight);
|
|
border-right: 1px solid var(--c-steel);
|
|
padding: var(--s-4) 0;
|
|
}
|
|
.sb-title { font-size: 10px; font-weight: 700; letter-spacing: 2px;
|
|
color: var(--c-fog); text-transform: uppercase;
|
|
padding: var(--s-3) var(--s-4) var(--s-2); }
|
|
.nav-item {
|
|
display: flex; align-items: center; gap: var(--s-2);
|
|
padding: 10px 16px;
|
|
color: var(--c-sand); font-size: 13px;
|
|
cursor: pointer; transition: background 120ms;
|
|
}
|
|
.nav-item:hover { background: var(--c-steel); }
|
|
.nav-item.active {
|
|
background: linear-gradient(90deg, rgba(255,176,32,0.14), transparent);
|
|
color: var(--c-foam);
|
|
box-shadow: inset 2px 0 0 var(--c-warn);
|
|
}
|
|
.nav-item .alarm-badge {
|
|
margin-left: auto;
|
|
padding: 2px 8px;
|
|
background: var(--c-emergency);
|
|
color: white;
|
|
border-radius: var(--r-pill);
|
|
font-size: 10px;
|
|
font-weight: 700;
|
|
}
|
|
|
|
/* Main */
|
|
.main {
|
|
grid-area: main;
|
|
display: grid;
|
|
grid-template-rows: auto auto 1fr;
|
|
overflow: hidden;
|
|
background: var(--g-deep-sea);
|
|
}
|
|
.summary {
|
|
padding: var(--s-5);
|
|
display: grid;
|
|
grid-template-columns: 1.4fr 1fr 1fr 1fr 1fr;
|
|
gap: var(--s-4);
|
|
}
|
|
.sum-emergency {
|
|
padding: var(--s-5);
|
|
border-radius: var(--r-3);
|
|
background: linear-gradient(135deg, rgba(255,59,71,0.18), rgba(255,59,71,0.04));
|
|
border: 1px solid rgba(255,59,71,0.45);
|
|
box-shadow: var(--glow-emergency);
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
.sum-emergency::before {
|
|
content: ""; position: absolute;
|
|
top: 0; left: 0; right: 0; height: 3px;
|
|
background: var(--g-emergency);
|
|
}
|
|
.sum-card {
|
|
padding: var(--s-5);
|
|
border-radius: var(--r-3);
|
|
background: var(--c-midnight);
|
|
border: 1px solid var(--c-steel);
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
.sum-card::before {
|
|
content: ""; position: absolute;
|
|
top: 0; left: 0; right: 0; height: 3px;
|
|
background: var(--c-iron);
|
|
}
|
|
.sum-card.high::before { background: var(--g-warn); filter: hue-rotate(-15deg); }
|
|
.sum-card.low::before { background: var(--g-warn); }
|
|
.sum-card.info::before { background: var(--c-info); }
|
|
.sum-card.ok::before { background: var(--g-ok); }
|
|
.sum-card .lbl {
|
|
font-size: 10px; letter-spacing: 2px; text-transform: uppercase;
|
|
color: var(--c-fog); font-weight: 700;
|
|
}
|
|
.sum-card .num {
|
|
font-family: var(--f-mono);
|
|
font-size: 56px;
|
|
font-weight: 600;
|
|
line-height: 1;
|
|
color: var(--c-foam);
|
|
letter-spacing: -2px;
|
|
margin: var(--s-3) 0 0;
|
|
}
|
|
.sum-card .sub { font-size: 11px; color: var(--c-fog); margin-top: 6px; }
|
|
.sum-emergency .num { color: var(--c-emergency); }
|
|
.sum-card.high .num { color: var(--c-high); }
|
|
.sum-card.low .num { color: var(--c-warn); }
|
|
.sum-card.info .num { color: var(--c-info); }
|
|
.sum-card.ok .num { color: var(--c-ok); }
|
|
|
|
/* Filters */
|
|
.filters {
|
|
padding: 0 var(--s-5) var(--s-3);
|
|
display: flex; gap: var(--s-2); align-items: center;
|
|
border-bottom: 1px solid var(--c-steel);
|
|
}
|
|
.filter-pill {
|
|
padding: 6px 14px;
|
|
border-radius: var(--r-pill);
|
|
background: var(--c-steel);
|
|
border: 1px solid transparent;
|
|
color: var(--c-sand);
|
|
font-size: 12px;
|
|
cursor: pointer;
|
|
transition: all 120ms;
|
|
}
|
|
.filter-pill:hover { background: var(--c-iron); }
|
|
.filter-pill.active {
|
|
background: rgba(0,217,255,0.12);
|
|
border-color: var(--c-cyan);
|
|
color: var(--c-cyan);
|
|
}
|
|
.filter-pill .count {
|
|
margin-left: 6px;
|
|
padding: 1px 6px;
|
|
background: rgba(0,0,0,0.3);
|
|
border-radius: var(--r-pill);
|
|
font-size: 10px;
|
|
font-family: var(--f-mono);
|
|
}
|
|
.filter-spacer { flex: 1; }
|
|
.search {
|
|
width: 280px;
|
|
padding: 8px 14px;
|
|
background: var(--c-steel);
|
|
border: 1px solid var(--c-iron);
|
|
border-radius: var(--r-pill);
|
|
color: var(--c-foam);
|
|
font-size: 12px;
|
|
outline: none;
|
|
}
|
|
.search:focus { border-color: var(--c-cyan); box-shadow: 0 0 0 3px rgba(0,217,255,0.18); }
|
|
|
|
/* Alarm list */
|
|
.alarm-list {
|
|
overflow-y: auto;
|
|
padding: 0;
|
|
}
|
|
.alarm-row {
|
|
display: grid;
|
|
grid-template-columns: 120px 120px 2fr 1fr auto;
|
|
gap: var(--s-4);
|
|
padding: var(--s-4) var(--s-5);
|
|
align-items: center;
|
|
border-bottom: 1px solid var(--c-steel);
|
|
transition: background 120ms;
|
|
}
|
|
.alarm-row:hover { background: rgba(255,255,255,0.02); }
|
|
.alarm-row.emergency {
|
|
background: linear-gradient(90deg, rgba(255,59,71,0.08), transparent 40%);
|
|
border-left: 3px solid var(--c-emergency);
|
|
}
|
|
.alarm-row.high {
|
|
background: linear-gradient(90deg, rgba(255,128,48,0.06), transparent 40%);
|
|
border-left: 3px solid var(--c-high);
|
|
}
|
|
.alarm-row.low {
|
|
background: linear-gradient(90deg, rgba(255,176,32,0.04), transparent 40%);
|
|
border-left: 3px solid var(--c-warn);
|
|
}
|
|
.alarm-row.info { border-left: 3px solid var(--c-info); }
|
|
.alarm-row.cleared { opacity: 0.55; }
|
|
.a-time {
|
|
font-family: var(--f-mono);
|
|
font-size: 12px;
|
|
color: var(--c-sand);
|
|
}
|
|
.a-time .ago { font-size: 10px; color: var(--c-fog); display: block; margin-top: 2px; }
|
|
.a-tag {
|
|
font-family: var(--f-mono);
|
|
font-size: 12px;
|
|
color: var(--c-cyan);
|
|
font-weight: 600;
|
|
}
|
|
.a-tag .sys { display: block; color: var(--c-fog); font-size: 10px; margin-top: 2px; font-weight: 400; }
|
|
.a-msg {
|
|
font-size: 14px;
|
|
color: var(--c-foam);
|
|
line-height: 1.4;
|
|
}
|
|
.a-msg .detail { display: block; color: var(--c-fog); font-size: 12px; margin-top: 2px; font-family: var(--f-mono); }
|
|
.a-state { display: flex; flex-direction: column; gap: 4px; }
|
|
.a-actions { display: flex; gap: var(--s-2); }
|
|
.btn-sm {
|
|
padding: 6px 14px;
|
|
font-size: 12px;
|
|
border-radius: var(--r-2);
|
|
border: 1px solid var(--c-iron);
|
|
background: transparent;
|
|
color: var(--c-sand);
|
|
cursor: pointer;
|
|
transition: all 120ms;
|
|
}
|
|
.btn-sm:hover { background: var(--c-steel); }
|
|
.btn-sm.primary { background: var(--g-cyan); border: none; color: #04111F; font-weight: 600; }
|
|
.btn-sm.danger { background: var(--g-emergency); border: none; color: var(--c-foam); }
|
|
|
|
.ticker {
|
|
grid-area: ticker;
|
|
display: flex; align-items: center; gap: var(--s-5);
|
|
padding: 0 var(--s-5);
|
|
background: var(--c-midnight);
|
|
border-top: 1px solid var(--c-steel);
|
|
font-family: var(--f-mono);
|
|
font-size: 11px;
|
|
color: var(--c-fog);
|
|
}
|
|
.ticker .sep { color: var(--c-iron); }
|
|
.ticker .pulse {
|
|
width: 8px; height: 8px;
|
|
border-radius: 50%;
|
|
background: var(--c-emergency);
|
|
box-shadow: var(--glow-emergency);
|
|
animation: heartbeat 1s ease-in-out infinite;
|
|
}
|
|
@keyframes heartbeat {
|
|
0%, 100% { transform: scale(1); opacity: 1; }
|
|
50% { transform: scale(1.5); opacity: 0.5; }
|
|
}
|
|
.tk-spacer { flex: 1; }
|
|
.ic { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 2; }
|
|
|
|
/* Pulse glow on critical row */
|
|
@keyframes pulseRow {
|
|
0%, 100% { background-color: rgba(255,59,71,0.08); }
|
|
50% { background-color: rgba(255,59,71,0.16); }
|
|
}
|
|
.alarm-row.emergency.active { animation: pulseRow 1500ms ease-in-out infinite; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="app-root rt">
|
|
|
|
<header class="topbar">
|
|
<div class="breadcrumb">
|
|
<img src="../brand/logo-mark.svg" alt="">
|
|
<strong>M/Y Aurora</strong>
|
|
<span class="sep">/</span>
|
|
<span class="system">Alarmas</span>
|
|
</div>
|
|
<div class="top-spacer"></div>
|
|
<div class="stats-strip">
|
|
<div class="item"><span class="num emergency">1</span><span class="lbl">Emergency</span></div>
|
|
<div class="item"><span class="num high">0</span><span class="lbl">High</span></div>
|
|
<div class="item"><span class="num low">2</span><span class="lbl">Low</span></div>
|
|
<div class="item"><span class="num info">1</span><span class="lbl">Info</span></div>
|
|
</div>
|
|
<button class="btn btn-secondary">
|
|
<svg class="ic" viewBox="0 0 24 24"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6M14 11v6"/></svg>
|
|
Limpiar resueltas
|
|
</button>
|
|
<button class="btn btn-primary">
|
|
<svg class="ic" viewBox="0 0 24 24" style="color:#04111F"><polyline points="20 6 9 17 4 12"/></svg>
|
|
ACK todas (no críticas)
|
|
</button>
|
|
</header>
|
|
|
|
<aside class="sidebar">
|
|
<div class="sb-title">Filtros rápidos</div>
|
|
<div class="nav-item active">
|
|
🔔 Activas no ACK
|
|
<span class="alarm-badge">4</span>
|
|
</div>
|
|
<div class="nav-item">📋 ACK pendientes resolución <span style="margin-left:auto; font-family: var(--f-mono); color: var(--c-fog); font-size: 11px;">1</span></div>
|
|
<div class="nav-item">✓ Resueltas 24h <span style="margin-left:auto; font-family: var(--f-mono); color: var(--c-fog); font-size: 11px;">18</span></div>
|
|
<div class="nav-item">📜 Histórico completo</div>
|
|
|
|
<div class="sb-title">Por prioridad</div>
|
|
<div class="nav-item"><span class="dot emergency"></span> Emergency <span style="margin-left:auto; font-family: var(--f-mono); color: var(--c-fog); font-size: 11px;">1</span></div>
|
|
<div class="nav-item"><span class="dot warn"></span> High <span style="margin-left:auto; font-family: var(--f-mono); color: var(--c-fog); font-size: 11px;">0</span></div>
|
|
<div class="nav-item"><span class="dot warn" style="background: var(--c-warn)"></span> Low <span style="margin-left:auto; font-family: var(--f-mono); color: var(--c-fog); font-size: 11px;">2</span></div>
|
|
<div class="nav-item"><span class="dot cyan"></span> Info <span style="margin-left:auto; font-family: var(--f-mono); color: var(--c-fog); font-size: 11px;">1</span></div>
|
|
|
|
<div class="sb-title">Por sistema</div>
|
|
<div class="nav-item">⚙ Máquina principal <span style="margin-left:auto; font-family: var(--f-mono); color: var(--c-fog); font-size: 11px;">1</span></div>
|
|
<div class="nav-item">⚡ Generación <span style="margin-left:auto; font-family: var(--f-mono); color: var(--c-fog); font-size: 11px;">2</span></div>
|
|
<div class="nav-item">🛁 Sentinas <span style="margin-left:auto; font-family: var(--f-mono); color: var(--c-fog); font-size: 11px;">1</span></div>
|
|
</aside>
|
|
|
|
<main class="main">
|
|
<div class="summary">
|
|
<div class="sum-emergency pulse-emergency">
|
|
<div class="lbl" style="color: var(--c-emergency);">🚨 Acción inmediata</div>
|
|
<div class="num">1</div>
|
|
<div class="sub" style="color: var(--c-sand);">ME_PORT.OIL_PRESS < 1.5 bar</div>
|
|
</div>
|
|
<div class="sum-card high">
|
|
<div class="lbl">High</div>
|
|
<div class="num">0</div>
|
|
<div class="sub">Sin alarmas alta</div>
|
|
</div>
|
|
<div class="sum-card low">
|
|
<div class="lbl">Low</div>
|
|
<div class="num">2</div>
|
|
<div class="sub">GEN coolant · BILGE</div>
|
|
</div>
|
|
<div class="sum-card info">
|
|
<div class="lbl">Info</div>
|
|
<div class="num">1</div>
|
|
<div class="sub">Shore power offline</div>
|
|
</div>
|
|
<div class="sum-card ok">
|
|
<div class="lbl">MTBF 7 días</div>
|
|
<div class="num">42<span style="font-size:18px;color:var(--c-fog);"> h</span></div>
|
|
<div class="sub">▲ +6h vs semana ant.</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="filters">
|
|
<button class="filter-pill active">Todas <span class="count">4</span></button>
|
|
<button class="filter-pill">Sin ACK <span class="count">4</span></button>
|
|
<button class="filter-pill">Emergency <span class="count">1</span></button>
|
|
<button class="filter-pill">High</button>
|
|
<button class="filter-pill">Low <span class="count">2</span></button>
|
|
<button class="filter-pill">Info <span class="count">1</span></button>
|
|
<div class="filter-spacer"></div>
|
|
<input class="search" placeholder="🔍 Buscar tag, mensaje o sistema…">
|
|
</div>
|
|
|
|
<div class="alarm-list">
|
|
|
|
<div class="alarm-row emergency active">
|
|
<div class="a-time">03:43:01<div class="ago">hace 12 s</div></div>
|
|
<div class="a-tag">ME_PORT.OIL_PRESS<div class="sys">Máquina principal</div></div>
|
|
<div class="a-msg">
|
|
Presión aceite crítica baja — <strong>1.2 bar</strong>
|
|
<span class="detail">threshold < 1.5 bar · hysteresis 0.2 · delay 2 s</span>
|
|
</div>
|
|
<div class="a-state">
|
|
<span class="badge badge-emergency">EMERGENCY</span>
|
|
<span class="caption" style="color:var(--c-emergency)">⚠ ACTIVE · sin ACK</span>
|
|
</div>
|
|
<div class="a-actions">
|
|
<button class="btn-sm danger">ACK</button>
|
|
<button class="btn-sm">Detalle</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="alarm-row low">
|
|
<div class="a-time">03:38:42<div class="ago">hace 5 min</div></div>
|
|
<div class="a-tag">GEN_1.COOLANT_TEMP<div class="sys">Generación</div></div>
|
|
<div class="a-msg">
|
|
Temperatura refrigerante alta — <strong>89°C</strong>
|
|
<span class="detail">threshold > 88°C · approaching emergency > 92°C</span>
|
|
</div>
|
|
<div class="a-state">
|
|
<span class="badge badge-low">LOW</span>
|
|
<span class="caption">ACTIVE · sin ACK</span>
|
|
</div>
|
|
<div class="a-actions">
|
|
<button class="btn-sm primary">ACK</button>
|
|
<button class="btn-sm">Detalle</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="alarm-row low">
|
|
<div class="a-time">03:31:17<div class="ago">hace 12 min</div></div>
|
|
<div class="a-tag">BILGE_MID.LEVEL<div class="sys">Sentinas</div></div>
|
|
<div class="a-msg">
|
|
Nivel sentina central — <strong>12%</strong>
|
|
<span class="detail">threshold > 10% · verificar pump cycle</span>
|
|
</div>
|
|
<div class="a-state">
|
|
<span class="badge badge-low">LOW</span>
|
|
<span class="caption">ACTIVE · sin ACK</span>
|
|
</div>
|
|
<div class="a-actions">
|
|
<button class="btn-sm primary">ACK</button>
|
|
<button class="btn-sm">Detalle</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="alarm-row info">
|
|
<div class="a-time">02:58:30<div class="ago">hace 45 min</div></div>
|
|
<div class="a-tag">SHORE_POWER.STATUS<div class="sys">Generación</div></div>
|
|
<div class="a-msg">
|
|
Transferencia a generador — <strong>desconexión muelle</strong>
|
|
<span class="detail">automatic transfer switch · GEN_1 picked up load</span>
|
|
</div>
|
|
<div class="a-state">
|
|
<span class="badge badge-info">INFO</span>
|
|
<span class="caption">ACTIVE · sin ACK</span>
|
|
</div>
|
|
<div class="a-actions">
|
|
<button class="btn-sm primary">ACK</button>
|
|
<button class="btn-sm">Detalle</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="alarm-row cleared">
|
|
<div class="a-time">03:12:04<div class="ago">hace 31 min</div></div>
|
|
<div class="a-tag">ME_PORT.OIL_TEMP<div class="sys">Máquina principal</div></div>
|
|
<div class="a-msg">
|
|
Temperatura aceite recuperada — <strong>88°C</strong>
|
|
<span class="detail">cleared at 03:18 · ACK por Carlos · duración 6 min</span>
|
|
</div>
|
|
<div class="a-state">
|
|
<span class="badge badge-muted">CLEARED</span>
|
|
<span class="caption" style="color:var(--c-ok)">✓ resolved</span>
|
|
</div>
|
|
<div class="a-actions">
|
|
<button class="btn-sm">Detalle</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="alarm-row cleared">
|
|
<div class="a-time">02:42:11<div class="ago">hace 1 h</div></div>
|
|
<div class="a-tag">GEN_1.VOLTAGE_L1<div class="sys">Generación</div></div>
|
|
<div class="a-msg">
|
|
Tensión L1 normalizada — <strong>231 V</strong>
|
|
<span class="detail">cleared at 02:45 · ACK por sistema · duración 3 min</span>
|
|
</div>
|
|
<div class="a-state">
|
|
<span class="badge badge-muted">CLEARED</span>
|
|
<span class="caption" style="color:var(--c-ok)">✓ resolved</span>
|
|
</div>
|
|
<div class="a-actions">
|
|
<button class="btn-sm">Detalle</button>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</main>
|
|
|
|
<footer class="ticker">
|
|
<span class="pulse"></span>
|
|
<span style="color: var(--c-emergency); font-weight: 700;">ALARMA CRÍTICA SIN ACK · ME_PORT.OIL_PRESS</span>
|
|
<span class="sep">|</span>
|
|
<span>Próxima escalación en 4:48</span>
|
|
<span class="tk-spacer"></span>
|
|
<span>Operador: Álvaro</span>
|
|
<span class="sep">|</span>
|
|
<span>v0.1.0.dev0</span>
|
|
</footer>
|
|
|
|
</div>
|
|
</body>
|
|
</html>
|