Files
tabler/shared/components/cards/SystemStatus.astro

115 lines
4.0 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
import CaptureScript from '../CaptureScript.astro'
interface Props {
id: string
services?: string[]
days?: number
seed?: number
class?: string
}
const { id, services = ['API', 'Dashboard', 'Webhooks', 'Email delivery'], days = 60, seed = 18, class: className } = Astro.props
const cardId = `system-status-${id}`
---
<div class:list={['card', className]} id={cardId}>
<div class="card-header">
<h3 class="card-title">System status</h3>
<div class="card-actions">
<span class="status status-green" data-status>
<span class="status-dot status-dot-animated"></span>
<span data-status-label>All systems operational</span>
</span>
</div>
</div>
<div class="list-group list-group-flush">
{
services.map((service) => (
<div class="list-group-item">
<div class="d-flex align-items-baseline mb-2">
<div class="fw-medium">{service}</div>
<div class="ms-auto text-secondary">
<span data-uptime-value>–</span> uptime
</div>
</div>
<div class="tracking" data-uptime={service} />
</div>
))
}
</div>
<div class="card-footer">
<div class="d-flex align-items-center text-secondary">
<div>{days} days ago</div>
<div class="legend-list mx-auto">
<span class="legend legend-green"><span class="legend-dot"></span>Operational</span>
<span class="legend legend-yellow"><span class="legend-dot"></span>Degraded</span>
<span class="legend legend-red"><span class="legend-dot"></span>Outage</span>
</div>
<div>Today</div>
</div>
</div>
</div>
<CaptureScript>
<!-- BEGIN SYSTEM STATUS -->
<script is:inline define:vars={{ cardId, days, seed }}>
// Share of days with an outage / degraded performance, and how much a degraded day counts
const OUTAGE_RATE = 0.02
const DEGRADED_RATE = 0.04
const DEGRADED_WEIGHT = 0.5
const STATUSES = {
up: { label: 'Operational', color: 'bg-green' },
degraded: { label: 'Degraded performance', color: 'bg-yellow' },
down: { label: 'Outage', color: 'bg-red' },
}
// Mulberry32: tiny seeded PRNG, so every load draws the same history
function createRandom(seed) {
return function () {
seed = (seed + 0x6d2b79f5) | 0
let t = Math.imul(seed ^ (seed >>> 15), 1 | seed)
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t
return ((t ^ (t >>> 14)) >>> 0) / 4294967296
}
}
function initSystemStatus() {
const card = document.getElementById(cardId)
const random = createRandom(seed)
const today = new Date()
let allOperational = true
card.querySelectorAll('[data-uptime]').forEach((container) => {
let score = 0
for (let i = days - 1; i >= 0; i--) {
const roll = random()
const status = roll < OUTAGE_RATE ? 'down' : roll < OUTAGE_RATE + DEGRADED_RATE ? 'degraded' : 'up'
const date = new Date(today.getFullYear(), today.getMonth(), today.getDate() - i)
const bar = document.createElement('div')
bar.className = 'tracking-block ' + STATUSES[status].color
bar.title = date.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' }) + ' – ' + STATUSES[status].label
container.appendChild(bar)
score += status === 'up' ? 1 : status === 'degraded' ? DEGRADED_WEIGHT : 0
}
if (score < days) allOperational = false
container.parentElement.querySelector('[data-uptime-value]').textContent = ((score / days) * 100).toFixed(2) + '%'
})
if (!allOperational) {
card.querySelector('[data-status]').classList.replace('status-green', 'status-yellow')
card.querySelector('[data-status-label]').textContent = 'Some systems affected'
}
}
document.readyState !== 'loading' ? initSystemStatus() : document.addEventListener('DOMContentLoaded', initSystemStatus, { once: true })
</script>
<!-- END SYSTEM STATUS -->
</CaptureScript>