feat: maintenance mode for monitors (#7)

This commit is contained in:
2026-04-17 18:42:23 +02:00
committed by GitHub
parent 0ab0221276
commit 3b074977ed
14 changed files with 279 additions and 22 deletions

View File

@@ -1,5 +1,7 @@
---
import type { ApiMonitorStatus } from '@worker/types';
// Allow maintenance as a valid runtime state:
type MonitorStatus = ApiMonitorStatus['status'] | 'maintenance';
import UptimeBars from './UptimeBars.astro';
interface Props {
@@ -41,13 +43,19 @@ const chartData = JSON.stringify({
<div class="monitor-head">
<div
class:list={['status-dot', `status-dot-${monitor.status}`]}
role="status"
aria-label={`Status: ${monitor.status === 'up' ? 'Operational' : monitor.status === 'down' ? 'Down' : 'Unknown'}`}
title={`${monitor.status === 'up' ? 'Operational' : monitor.status === 'down' ? 'Down' : 'Unknown'}`}
></div>
<h3 class="monitor-name" id={`monitor-${monitor.name.replace(/\s+/g, '-').toLowerCase()}-title`} title={monitor.name}>{monitor.name}</h3>
<span class:list={['monitor-uptime', `uptime-${monitor.status}`]}>{uptimeFormatted}%</span>
<span class="monitor-meta">{lastCheckedText}</span>
role="status"
aria-label={`Status: ${(monitor.status as any) === 'up' ? 'Operational' : (monitor.status as any) === 'down' ? 'Down' : (monitor.status as any) === 'maintenance' ? 'Maintenance' : 'Unknown'}`}
title={`${(monitor.status as any) === 'up' ? 'Operational' : (monitor.status as any) === 'down' ? 'Down' : (monitor.status as any) === 'maintenance' ? 'Maintenance' : 'Unknown'}`}
></div>
<h3 class="monitor-name" id={`monitor-${monitor.name.replace(/\s+/g, '-').toLowerCase()}-title`} title={monitor.name}>
{monitor.name}
{(monitor.status as any) === 'maintenance' && (
<span class="maintenance-badge" title="Scheduled Maintenance">Maintenance</span>
)}
</h3>
<span class:list={['monitor-uptime', `uptime-${monitor.status}`]} aria-label={(monitor.status as any) === 'maintenance' ? 'Scheduled Maintenance - uptime value reflects unmonitored state' : undefined}>{uptimeFormatted}%</span>
<span class="monitor-meta">{lastCheckedText}</span>
</div>
<UptimeBars dailyHistory={monitor.dailyHistory} />
@@ -166,6 +174,34 @@ const chartData = JSON.stringify({
animation: pulse-glow 3s ease-in-out infinite;
}
.status-dot-maintenance {
background: var(--maintenance, #bfa21a);
box-shadow: 0 0 16px #ffe066cc, 0 0 32px #bfa21ab0;
border-color: #f8e16c;
}
.status-dot-maintenance::after {
background: var(--maintenance, #f8e16c);
animation: pulse-glow 1.5s ease-in-out infinite;
opacity: 0.4;
}
.uptime-maintenance {
color: var(--maintenance, #bfa21a);
}
.maintenance-badge {
display: inline-block;
margin-left: 0.5em;
padding: 0.1em 0.5em;
font-size: var(--text-2xs, 12px);
background: var(--maintenance, #fff5bf);
color: var(--maintenance, #bfa21a);
border-radius: 4px;
font-weight: 600;
letter-spacing: 0.03em;
vertical-align: middle;
}
@keyframes pulse-glow {
0%, 100% {
opacity: 0.3;

View File

@@ -32,7 +32,7 @@ try {
}
// Sort: down monitors first, then unknown, then up
const sortOrder = { down: 0, unknown: 1, up: 2 } as const;
const sortOrder = { down: 0, maintenance: 1, unknown: 2, up: 3 } as const;
const sortedMonitors = data ? [...data.monitors].sort(
(a, b) => (sortOrder[a.status] ?? 1) - (sortOrder[b.status] ?? 1)
) : [];