diff --git a/apps/status/src/static/app.css b/apps/status/src/static/app.css index 52d5a2d..0d6667a 100644 --- a/apps/status/src/static/app.css +++ b/apps/status/src/static/app.css @@ -44,7 +44,19 @@ h1 { font-size: 1.75rem; font-weight: 700; margin: 0 0 0.5rem; } .overall { padding: 1rem 1.25rem; border-radius: 10px; font-weight: 600; font-size: 1rem; margin: 1.5rem 0 2rem; display: flex; align-items: center; gap: 0.75rem; border: 1px solid; } .overall .dot { width: 12px; height: 12px; border-radius: 50%; flex-shrink: 0; animation: pulse-dot 2s ease-in-out infinite; } @keyframes pulse-dot { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } -.group-title { font-size: 0.85rem; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em; margin: 2rem 0 0.75rem; } +.group-section { margin: 1.5rem 0; background: var(--card); border: 1px solid var(--border); border-radius: 10px; overflow: hidden; } +.group-toggle { display: none; } +.group-header { display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 1.25rem; cursor: pointer; } +.group-header:hover { background: rgba(255,255,255,0.02); } +.group-header-left { display: flex; align-items: center; gap: 0.5rem; } +.group-chev { color: var(--muted); transition: transform 0.15s; flex-shrink: 0; } +.group-toggle:checked ~ .group-header .group-chev { transform: rotate(90deg); } +.group-name { font-size: 0.85rem; font-weight: 600; color: var(--fg); } +.group-status { font-size: 0.75rem; font-weight: 600; } +.group-body { display: none; padding: 0 0.75rem 0.75rem; } +.group-toggle:checked ~ .group-body { display: block; } +.group-body .monitors { gap: 0.35rem; } +.group-body .monitor { border-radius: 8px; } .monitors { display: flex; flex-direction: column; gap: 0.5rem; } /* All monitors share one structure: a clickable header row + a collapsible detail panel. display_mode just controls whether `expanded-state` is set diff --git a/apps/status/src/views/page.ejs b/apps/status/src/views/page.ejs index b61760e..6a9c55b 100644 --- a/apps/status/src/views/page.ejs +++ b/apps/status/src/views/page.ejs @@ -181,12 +181,29 @@ return d.toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } %> - <% groupOrder.forEach(function(gid) { + <% groupOrder.forEach(function(gid, gi) { const list = grouped[gid]; if (!list || list.length === 0) return; const groupName = gid ? (groups.find(g => g.id === gid)?.name || '') : ''; + // Aggregate status for the group header + const activeInGroup = list.filter(m => m.current_state !== 'paused'); + const downInGroup = activeInGroup.filter(m => m.current_state === 'down').length; + const groupStatus = activeInGroup.length === 0 ? 'unknown' : downInGroup === activeInGroup.length ? 'down' : downInGroup > 0 ? 'degraded' : 'up'; + const groupStatusLabel = groupStatus === 'up' ? 'Operational' : groupStatus === 'degraded' ? 'Degraded' : groupStatus === 'down' ? 'Down' : ''; + const groupStatusColor = groupStatus === 'up' ? 'var(--bar-up)' : groupStatus === 'degraded' ? 'var(--bar-partial)' : groupStatus === 'down' ? 'var(--bar-down)' : 'var(--muted)'; %> - <% if (groupName) { %>
The heartbeat bar shows the last N hours or days. The four uptime cells (24h / 7d / 30d / 90d) are independent and always present.
+Monitors can be organized into collapsible groups on the public page. Drag to reorder.
+Tick to attach. Drag to reorder. "Show as" overrides the name on this page only. "Mode" picks compact or expanded for that one monitor (or leave blank to use the page default).
+Tick to attach. Drag to reorder. Assign to a group, override the display name, or pick a display mode per monitor.
<% if (allMonitors.length === 0) { %>No monitors yet. Create one first.
<% } else { %> @@ -102,6 +126,7 @@ const isAttached = attached.has(m.id); const displayName = displayNames[m.id] || ''; const displayMode = displayModes[m.id] || ''; + const groupIdx = groupIdToIndex[monitorGroups[m.id]] || ''; %>