Server fleet status board
A monitoring wall in one table. Six hosts in an
md-table, each row carrying an
md-status-dot beside the state word, CPU and
memory as md-meter bars that recolour at 75% and 90%
thresholds, a p95-latency md-sparkline on a shared
0–250 ms scale, and an md-tooltip with the last
health-check result. A segmented button filters by region (the table’s FLIP
motion animates rows out), and the refresh button shows an
md-loading-indicator while new readings
land.
Live preview — switch regions, hover a status for the last check, or refresh for new readings
Production fleet
Updated 08:40:12 UTC
Show code for each technology
<!-- index.html <head> — the icon font the components draw from -->
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&display=swap">
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div class="fleet-toolbar">
<md-loading-indicator hidden label="Refreshing readings"></md-loading-indicator>
<md-segmented-button-set aria-label="Region filter" density="-1">
<md-segmented-button value="all" label="All" selected></md-segmented-button>
<md-segmented-button value="us-east" label="us-east"></md-segmented-button>
<md-segmented-button value="eu-west" label="eu-west"></md-segmented-button>
</md-segmented-button-set>
<md-button variant="tonal" icon="refresh">Refresh</md-button>
</div>
<md-table-container variant="outlined">
<md-table label="Server fleet status" min-width="640px"
column-template="minmax(130px, 1.2fr) 150px minmax(100px, 1fr) minmax(100px, 1fr) 130px">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Host</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
<md-table-cell head scope="col">CPU</md-table-cell>
<md-table-cell head scope="col">Memory</md-table-cell>
<md-table-cell head scope="col">Latency p95</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
<md-table-row value="edge-01">
<md-table-cell ellipsis>edge-01.use1</md-table-cell>
<md-table-cell>
<md-tooltip text="Last check 08:40:12 UTC — agent v2.14, HTTP 200 in 38 ms" position="bottom">
<span tabindex="0" class="host-status">
<span class="dot-anchor"><md-status-dot state="online"></md-status-dot></span>
Healthy
</span>
</md-tooltip>
</md-table-cell>
<md-table-cell><md-meter label="CPU load, edge-01" value="34" show-value color="success"></md-meter></md-table-cell>
<md-table-cell><md-meter label="Memory used, edge-01" value="61" show-value color="success"></md-meter></md-table-cell>
<md-table-cell><md-sparkline min="0" max="250" height="24px"></md-sparkline></md-table-cell>
</md-table-row>
<md-table-row value="cache-01">
<md-table-cell ellipsis>cache-01.euw1</md-table-cell>
<md-table-cell>
<md-tooltip text="Last check 08:40:12 UTC — connection refused after 3 retries" position="bottom">
<span tabindex="0" class="host-status">
<span class="dot-anchor"><md-status-dot state="busy" live></md-status-dot></span>
Down
</span>
</md-tooltip>
</md-table-cell>
<md-table-cell><md-meter label="CPU load, cache-01" value="0" value-text="no data" show-value></md-meter></md-table-cell>
<md-table-cell><md-meter label="Memory used, cache-01" value="0" value-text="no data" show-value></md-meter></md-table-cell>
<md-table-cell><md-sparkline min="0" max="250" height="24px" color="error"></md-sparkline></md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-table-container>
<script type="module">
const table = document.querySelector('[data-fleet]');
const filter = document.querySelector('[data-region-filter]');
const refreshBtn = document.querySelector('[data-refresh]');
const spinner = document.querySelector('[data-refreshing]');
const updated = document.querySelector('[data-updated]');
const rows = Array.from(document.querySelectorAll('md-table-row[data-host]'));
// Eight readings per host, 5-minute intervals; shared 0-250 ms scale.
const latency = {
'edge-01': [36, 41, 38, 35, 39, 37, 40, 38],
'api-01': [48, 55, 51, 47, 58, 54, 50, 52],
'queue-01': [62, 71, 90, 118, 141, 156, 174, 187],
'db-01': [42, 40, 45, 43, 47, 44, 41, 44],
'cache-01': [58, 61, 66, 84, 140, 231, 244, 250],
'worker-02': [66, 59, 63, 70, 57, 62, 64, 61]
};
const times = ['08:05', '08:10', '08:15', '08:20', '08:25', '08:30', '08:35', '08:40'];
rows.forEach((row) => {
const spark = row.querySelector('md-sparkline');
spark.data = latency[row.getAttribute('data-host')].slice();
spark.labels = times.slice();
spark.valueFormatter = (v) => (v == null ? 'no data' : v + ' ms');
});
function meterColor(v) {
return v >= 90 ? 'error' : v >= 75 ? 'warning' : 'success';
}
function jitter(v, lo, hi) {
const next = v + Math.round(Math.random() * 12 - 6);
return Math.min(hi, Math.max(lo, next));
}
// Region filter: arm the FLIP snapshot, then mutate the rows.
filter.addEventListener('mdChange', (e) => {
const region = e.detail[0] || 'all';
table.animateNextChange().then(() => {
rows.forEach((row) => {
const match = region === 'all' || row.getAttribute('data-region') === region;
row.style.display = match ? '' : 'none';
});
});
});
// Refresh: brief indeterminate wait, then new readings everywhere.
refreshBtn.addEventListener('click', () => {
spinner.hidden = false;
refreshBtn.disabled = true;
setTimeout(() => {
const time = new Date().toISOString().slice(11, 19);
rows.forEach((row) => {
const tip = row.querySelector('md-tooltip');
const spark = row.querySelector('md-sparkline');
const series = latency[row.getAttribute('data-host')];
if (row.hasAttribute('data-down')) {
tip.text = 'Last check ' + time + ' UTC — connection refused after 3 retries';
return;
}
const meters = row.querySelectorAll('md-meter');
meters.forEach((m) => {
const next = jitter(m.value, 5, 98);
m.value = next;
m.color = meterColor(next);
});
series.shift();
series.push(jitter(series[series.length - 1], 20, 240));
spark.data = series.slice();
const ping = series[series.length - 1];
tip.text = 'Last check ' + time + ' UTC — agent v2.14, HTTP 200 in ' + ping + ' ms';
});
updated.textContent = 'Updated ' + time + ' UTC';
spinner.hidden = true;
refreshBtn.disabled = false;
}, 900);
});
</script>import { useRef, useState } from 'react';
import {
MdButton, MdLoadingIndicator, MdMeter, MdSegmentedButton, MdSegmentedButtonSet,
MdSparkline, MdStatusDot, MdTable, MdTableBody, MdTableCell, MdTableContainer,
MdTableHead, MdTableRow, MdTooltip,
} from '@awc-ui/react';
const TIMES = ['08:05', '08:10', '08:15', '08:20', '08:25', '08:30', '08:35', '08:40'];
const DOT = { healthy: 'online', degraded: 'away', down: 'busy' };
const WORD = { healthy: 'Healthy', degraded: 'Degraded', down: 'Down' };
const SPARK = { healthy: 'primary', degraded: 'warning', down: 'error' };
const INITIAL = [
{ id: 'edge-01', name: 'edge-01.use1', region: 'us-east', status: 'healthy', cpu: 34, mem: 61,
latency: [36, 41, 38, 35, 39, 37, 40, 38], check: 'Last check 08:40:12 UTC — agent v2.14, HTTP 200 in 38 ms' },
{ id: 'queue-01', name: 'queue-01.use1', region: 'us-east', status: 'degraded', cpu: 88, mem: 74,
latency: [62, 71, 90, 118, 141, 156, 174, 187], check: 'Last check 08:40:11 UTC — agent v2.14, HTTP 200 in 187 ms (slow)' },
{ id: 'db-01', name: 'db-01.euw1', region: 'eu-west', status: 'healthy', cpu: 23, mem: 79,
latency: [42, 40, 45, 43, 47, 44, 41, 44], check: 'Last check 08:40:09 UTC — agent v2.14, HTTP 200 in 44 ms' },
{ id: 'cache-01', name: 'cache-01.euw1', region: 'eu-west', status: 'down', cpu: null, mem: null,
latency: [58, 61, 66, 84, 140, 231, 244, 250], check: 'Last check 08:40:12 UTC — connection refused after 3 retries' },
];
const meterColor = (v) => (v == null ? 'primary' : v >= 90 ? 'error' : v >= 75 ? 'warning' : 'success');
const jitter = (v, lo, hi) => Math.min(hi, Math.max(lo, v + Math.round(Math.random() * 12 - 6)));
const fmtMs = (v) => (v == null ? 'no data' : v + ' ms');
export default function FleetBoard() {
const tableRef = useRef(null);
const [hosts, setHosts] = useState(INITIAL);
const [region, setRegion] = useState('all');
const [refreshing, setRefreshing] = useState(false);
const [updated, setUpdated] = useState('08:40:12');
const visible = hosts.filter((h) => region === 'all' || h.region === region);
const onRegion = (e) => {
// Arm the FLIP snapshot first, then let React drop the filtered rows.
const next = e.detail[0] || 'all';
tableRef.current.animateNextChange().then(() => setRegion(next));
};
const onRefresh = () => {
setRefreshing(true);
setTimeout(() => { // stands in for the real readings fetch
const time = new Date().toISOString().slice(11, 19);
setHosts((prev) => prev.map((h) => {
if (h.status === 'down') return { ...h, check: 'Last check ' + time + ' UTC — connection refused after 3 retries' };
const latency = h.latency.slice(1).concat(jitter(h.latency[7], 20, 240));
return { ...h, latency, cpu: jitter(h.cpu, 5, 98), mem: jitter(h.mem, 5, 98),
check: 'Last check ' + time + ' UTC — agent v2.14, HTTP 200 in ' + latency[7] + ' ms' };
}));
setUpdated(time);
setRefreshing(false);
}, 900);
};
return (
<div style={{ display: 'grid', gap: 16 }}>
<div style={{ display: 'flex', flexWrap: 'wrap', alignItems: 'center', justifyContent: 'space-between', gap: 12 }}>
<span>Updated {updated} UTC</span>
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
<MdLoadingIndicator hidden={!refreshing} label="Refreshing readings" />
<MdSegmentedButtonSet aria-label="Region filter" density={-1} onMdChange={onRegion}>
<MdSegmentedButton value="all" label="All" selected={region === 'all'} />
<MdSegmentedButton value="us-east" label="us-east" selected={region === 'us-east'} />
<MdSegmentedButton value="eu-west" label="eu-west" selected={region === 'eu-west'} />
</MdSegmentedButtonSet>
<MdButton variant="tonal" icon="refresh" disabled={refreshing} onMdClick={onRefresh}>Refresh</MdButton>
</div>
</div>
<MdTableContainer variant="outlined">
<MdTable ref={tableRef} label="Server fleet status" minWidth="640px"
columnTemplate="minmax(130px, 1.2fr) 150px minmax(100px, 1fr) minmax(100px, 1fr) 130px">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell head scope="col">Host</MdTableCell>
<MdTableCell head scope="col">Status</MdTableCell>
<MdTableCell head scope="col">CPU</MdTableCell>
<MdTableCell head scope="col">Memory</MdTableCell>
<MdTableCell head scope="col">Latency p95</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
{visible.map((h) => (
<MdTableRow key={h.id} value={h.id}>
<MdTableCell ellipsis>{h.name}</MdTableCell>
<MdTableCell>
<MdTooltip text={h.check} position="bottom">
<span tabIndex={0} style={{ display: 'inline-flex', alignItems: 'center', gap: 10 }}>
<MdStatusDot state={DOT[h.status]} live={h.status === 'down'} />
{WORD[h.status]}
</span>
</MdTooltip>
</MdTableCell>
<MdTableCell>
<MdMeter label={'CPU load, ' + h.id} value={h.cpu ?? 0} showValue
valueText={h.cpu == null ? 'no data' : undefined} color={meterColor(h.cpu)} />
</MdTableCell>
<MdTableCell>
<MdMeter label={'Memory used, ' + h.id} value={h.mem ?? 0} showValue
valueText={h.mem == null ? 'no data' : undefined} color={meterColor(h.mem)} />
</MdTableCell>
<MdTableCell>
<MdSparkline min={0} max={250} heightProp="24px" color={SPARK[h.status]}
data={h.latency} labels={TIMES} valueFormatter={fmtMs} />
</MdTableCell>
</MdTableRow>
))}
</MdTableBody>
</MdTable>
</MdTableContainer>
</div>
);
}// fleet-board.component.ts — standalone; the md-* tags are used directly
// (elements registered once in main.ts: import '@awc-ui/core/define';)
import { Component, CUSTOM_ELEMENTS_SCHEMA, ElementRef, ViewChild, computed, signal } from '@angular/core';
interface Host { id: string; name: string; region: string; status: 'healthy' | 'degraded' | 'down';
cpu: number | null; mem: number | null; latency: number[]; check: string; }
const HOSTS: Host[] = [
{ id: 'edge-01', name: 'edge-01.use1', region: 'us-east', status: 'healthy', cpu: 34, mem: 61,
latency: [36, 41, 38, 35, 39, 37, 40, 38], check: 'Last check 08:40:12 UTC — agent v2.14, HTTP 200 in 38 ms' },
{ id: 'queue-01', name: 'queue-01.use1', region: 'us-east', status: 'degraded', cpu: 88, mem: 74,
latency: [62, 71, 90, 118, 141, 156, 174, 187], check: 'Last check 08:40:11 UTC — agent v2.14, HTTP 200 in 187 ms (slow)' },
{ id: 'db-01', name: 'db-01.euw1', region: 'eu-west', status: 'healthy', cpu: 23, mem: 79,
latency: [42, 40, 45, 43, 47, 44, 41, 44], check: 'Last check 08:40:09 UTC — agent v2.14, HTTP 200 in 44 ms' },
{ id: 'cache-01', name: 'cache-01.euw1', region: 'eu-west', status: 'down', cpu: null, mem: null,
latency: [58, 61, 66, 84, 140, 231, 244, 250], check: 'Last check 08:40:12 UTC — connection refused after 3 retries' },
];
@Component({
selector: 'app-fleet-board',
standalone: true,
// Without this the compiler rejects the property bindings on md-* tags.
schemas: [CUSTOM_ELEMENTS_SCHEMA],
templateUrl: './fleet-board.component.html',
})
export class FleetBoardComponent {
@ViewChild('fleet') fleet!: ElementRef;
times = ['08:05', '08:10', '08:15', '08:20', '08:25', '08:30', '08:35', '08:40'];
dot = { healthy: 'online', degraded: 'away', down: 'busy' } as const;
word = { healthy: 'Healthy', degraded: 'Degraded', down: 'Down' } as const;
spark = { healthy: 'primary', degraded: 'warning', down: 'error' } as const;
hosts = signal<Host[]>(HOSTS);
region = signal('all');
refreshing = signal(false);
updated = signal('08:40:12');
visible = computed(() => this.hosts().filter((h) => this.region() === 'all' || h.region === this.region()));
fmtMs = (v: number | null) => (v == null ? 'no data' : v + ' ms');
meterColor(v: number | null) { return v == null ? 'primary' : v >= 90 ? 'error' : v >= 75 ? 'warning' : 'success'; }
private jitter(v: number, lo: number, hi: number) { return Math.min(hi, Math.max(lo, v + Math.round(Math.random() * 12 - 6))); }
onRegion(e: Event) {
const next = (e as CustomEvent<string[]>).detail[0] || 'all';
// Arm the FLIP snapshot, then mutate the row list.
this.fleet.nativeElement.animateNextChange().then(() => this.region.set(next));
}
refresh() {
this.refreshing.set(true);
setTimeout(() => { // stands in for the real readings fetch
const time = new Date().toISOString().slice(11, 19);
this.hosts.update((hosts) => hosts.map((h) => {
if (h.status === 'down') return { ...h, check: 'Last check ' + time + ' UTC — connection refused after 3 retries' };
const latency = h.latency.slice(1).concat(this.jitter(h.latency[7], 20, 240));
return { ...h, latency, cpu: this.jitter(h.cpu!, 5, 98), mem: this.jitter(h.mem!, 5, 98),
check: 'Last check ' + time + ' UTC — agent v2.14, HTTP 200 in ' + latency[7] + ' ms' };
}));
this.updated.set(time);
this.refreshing.set(false);
}, 900);
}
}
<!-- fleet-board.component.html -->
<div class="fleet-toolbar">
<span>Updated {{ updated() }} UTC</span>
<md-loading-indicator [hidden]="!refreshing()" label="Refreshing readings"></md-loading-indicator>
<md-segmented-button-set aria-label="Region filter" density="-1" (mdChange)="onRegion($event)">
<md-segmented-button value="all" label="All" [selected]="region() === 'all'"></md-segmented-button>
<md-segmented-button value="us-east" label="us-east" [selected]="region() === 'us-east'"></md-segmented-button>
<md-segmented-button value="eu-west" label="eu-west" [selected]="region() === 'eu-west'"></md-segmented-button>
</md-segmented-button-set>
<md-button variant="tonal" icon="refresh" [disabled]="refreshing()" (mdClick)="refresh()">Refresh</md-button>
</div>
<md-table-container variant="outlined">
<md-table #fleet label="Server fleet status" min-width="640px"
column-template="minmax(130px, 1.2fr) 150px minmax(100px, 1fr) minmax(100px, 1fr) 130px">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Host</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
<md-table-cell head scope="col">CPU</md-table-cell>
<md-table-cell head scope="col">Memory</md-table-cell>
<md-table-cell head scope="col">Latency p95</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
@for (h of visible(); track h.id) {
<md-table-row [value]="h.id">
<md-table-cell ellipsis>{{ h.name }}</md-table-cell>
<md-table-cell>
<md-tooltip [text]="h.check" position="bottom">
<span tabindex="0" class="host-status">
<md-status-dot [state]="dot[h.status]" [live]="h.status === 'down'"></md-status-dot>
{{ word[h.status] }}
</span>
</md-tooltip>
</md-table-cell>
<md-table-cell>
<md-meter [label]="'CPU load, ' + h.id" [value]="h.cpu ?? 0" show-value
[valueText]="h.cpu == null ? 'no data' : ''" [color]="meterColor(h.cpu)"></md-meter>
</md-table-cell>
<md-table-cell>
<md-meter [label]="'Memory used, ' + h.id" [value]="h.mem ?? 0" show-value
[valueText]="h.mem == null ? 'no data' : ''" [color]="meterColor(h.mem)"></md-meter>
</md-table-cell>
<md-table-cell>
<md-sparkline min="0" max="250" height="24px" [color]="spark[h.status]"
[data]="h.latency" [labels]="times" [valueFormatter]="fmtMs"></md-sparkline>
</md-table-cell>
</md-table-row>
}
</md-table-body>
</md-table>
</md-table-container><script setup lang="ts">
import { computed, ref } from 'vue';
import {
MdButton, MdLoadingIndicator, MdMeter, MdSegmentedButton, MdSegmentedButtonSet,
MdSparkline, MdStatusDot, MdTable, MdTableBody, MdTableCell, MdTableContainer,
MdTableHead, MdTableRow, MdTooltip,
} from '@awc-ui/vue';
const TIMES = ['08:05', '08:10', '08:15', '08:20', '08:25', '08:30', '08:35', '08:40'];
const DOT = { healthy: 'online', degraded: 'away', down: 'busy' };
const WORD = { healthy: 'Healthy', degraded: 'Degraded', down: 'Down' };
const SPARK = { healthy: 'primary', degraded: 'warning', down: 'error' };
const hosts = ref([
{ id: 'edge-01', name: 'edge-01.use1', region: 'us-east', status: 'healthy', cpu: 34, mem: 61,
latency: [36, 41, 38, 35, 39, 37, 40, 38], check: 'Last check 08:40:12 UTC — agent v2.14, HTTP 200 in 38 ms' },
{ id: 'queue-01', name: 'queue-01.use1', region: 'us-east', status: 'degraded', cpu: 88, mem: 74,
latency: [62, 71, 90, 118, 141, 156, 174, 187], check: 'Last check 08:40:11 UTC — agent v2.14, HTTP 200 in 187 ms (slow)' },
{ id: 'db-01', name: 'db-01.euw1', region: 'eu-west', status: 'healthy', cpu: 23, mem: 79,
latency: [42, 40, 45, 43, 47, 44, 41, 44], check: 'Last check 08:40:09 UTC — agent v2.14, HTTP 200 in 44 ms' },
{ id: 'cache-01', name: 'cache-01.euw1', region: 'eu-west', status: 'down', cpu: null, mem: null,
latency: [58, 61, 66, 84, 140, 231, 244, 250], check: 'Last check 08:40:12 UTC — connection refused after 3 retries' },
]);
const region = ref('all');
const refreshing = ref(false);
const updated = ref('08:40:12');
const table = ref();
const visible = computed(() => hosts.value.filter((h) => region.value === 'all' || h.region === region.value));
const fmtMs = (v: number | null) => (v == null ? 'no data' : v + ' ms');
const meterColor = (v: number | null) => (v == null ? 'primary' : v >= 90 ? 'error' : v >= 75 ? 'warning' : 'success');
const jitter = (v: number, lo: number, hi: number) => Math.min(hi, Math.max(lo, v + Math.round(Math.random() * 12 - 6)));
function onRegion(e: CustomEvent<string[]>) {
const next = e.detail[0] || 'all';
// The wrapper's $el is the md-table element — arm the FLIP, then filter.
table.value.$el.animateNextChange().then(() => { region.value = next; });
}
function refresh() {
refreshing.value = true;
setTimeout(() => { // stands in for the real readings fetch
const time = new Date().toISOString().slice(11, 19);
hosts.value = hosts.value.map((h) => {
if (h.status === 'down') return { ...h, check: 'Last check ' + time + ' UTC — connection refused after 3 retries' };
const latency = h.latency.slice(1).concat(jitter(h.latency[7], 20, 240));
return { ...h, latency, cpu: jitter(h.cpu, 5, 98), mem: jitter(h.mem, 5, 98),
check: 'Last check ' + time + ' UTC — agent v2.14, HTTP 200 in ' + latency[7] + ' ms' };
});
updated.value = time;
refreshing.value = false;
}, 900);
}
</script>
<template>
<div class="fleet-toolbar">
<span>Updated {{ updated }} UTC</span>
<MdLoadingIndicator :hidden="!refreshing" label="Refreshing readings" />
<MdSegmentedButtonSet aria-label="Region filter" :density="-1" @md-change="onRegion">
<MdSegmentedButton value="all" label="All" :selected="region === 'all'" />
<MdSegmentedButton value="us-east" label="us-east" :selected="region === 'us-east'" />
<MdSegmentedButton value="eu-west" label="eu-west" :selected="region === 'eu-west'" />
</MdSegmentedButtonSet>
<MdButton variant="tonal" icon="refresh" :disabled="refreshing" @md-click="refresh">Refresh</MdButton>
</div>
<MdTableContainer variant="outlined">
<MdTable ref="table" label="Server fleet status" minWidth="640px"
columnTemplate="minmax(130px, 1.2fr) 150px minmax(100px, 1fr) minmax(100px, 1fr) 130px">
<MdTableHead>
<MdTableRow rowgroup="head">
<MdTableCell :head="true" scope="col">Host</MdTableCell>
<MdTableCell :head="true" scope="col">Status</MdTableCell>
<MdTableCell :head="true" scope="col">CPU</MdTableCell>
<MdTableCell :head="true" scope="col">Memory</MdTableCell>
<MdTableCell :head="true" scope="col">Latency p95</MdTableCell>
</MdTableRow>
</MdTableHead>
<MdTableBody>
<MdTableRow v-for="h in visible" :key="h.id" :value="h.id">
<MdTableCell :ellipsis="true">{{ h.name }}</MdTableCell>
<MdTableCell>
<MdTooltip :text="h.check" position="bottom">
<span tabindex="0" class="host-status">
<MdStatusDot :state="DOT[h.status]" :live="h.status === 'down'" />
{{ WORD[h.status] }}
</span>
</MdTooltip>
</MdTableCell>
<MdTableCell>
<MdMeter :label="'CPU load, ' + h.id" :value="h.cpu ?? 0" :showValue="true"
:valueText="h.cpu == null ? 'no data' : ''" :color="meterColor(h.cpu)" />
</MdTableCell>
<MdTableCell>
<MdMeter :label="'Memory used, ' + h.id" :value="h.mem ?? 0" :showValue="true"
:valueText="h.mem == null ? 'no data' : ''" :color="meterColor(h.mem)" />
</MdTableCell>
<MdTableCell>
<MdSparkline :min="0" :max="250" heightProp="24px" :color="SPARK[h.status]"
:data="h.latency" :labels="TIMES" :valueFormatter="fmtMs" />
</MdTableCell>
</MdTableRow>
</MdTableBody>
</MdTable>
</MdTableContainer>
</template><script>
// Register the md-* elements once on the client. In SvelteKit, guard it:
// if (browser) defineCustomElements(window);
import { defineCustomElements } from '@awc-ui/svelte';
defineCustomElements(window);
const TIMES = ['08:05', '08:10', '08:15', '08:20', '08:25', '08:30', '08:35', '08:40'];
const DOT = { healthy: 'online', degraded: 'away', down: 'busy' };
const WORD = { healthy: 'Healthy', degraded: 'Degraded', down: 'Down' };
const SPARK = { healthy: 'primary', degraded: 'warning', down: 'error' };
let hosts = [
{ id: 'edge-01', name: 'edge-01.use1', region: 'us-east', status: 'healthy', cpu: 34, mem: 61,
latency: [36, 41, 38, 35, 39, 37, 40, 38], check: 'Last check 08:40:12 UTC — agent v2.14, HTTP 200 in 38 ms' },
{ id: 'queue-01', name: 'queue-01.use1', region: 'us-east', status: 'degraded', cpu: 88, mem: 74,
latency: [62, 71, 90, 118, 141, 156, 174, 187], check: 'Last check 08:40:11 UTC — agent v2.14, HTTP 200 in 187 ms (slow)' },
{ id: 'db-01', name: 'db-01.euw1', region: 'eu-west', status: 'healthy', cpu: 23, mem: 79,
latency: [42, 40, 45, 43, 47, 44, 41, 44], check: 'Last check 08:40:09 UTC — agent v2.14, HTTP 200 in 44 ms' },
{ id: 'cache-01', name: 'cache-01.euw1', region: 'eu-west', status: 'down', cpu: null, mem: null,
latency: [58, 61, 66, 84, 140, 231, 244, 250], check: 'Last check 08:40:12 UTC — connection refused after 3 retries' },
];
let region = 'all';
let refreshing = false;
let updated = '08:40:12';
let table;
$: visible = hosts.filter((h) => region === 'all' || h.region === region);
const fmtMs = (v) => (v == null ? 'no data' : v + ' ms');
const meterColor = (v) => (v == null ? 'primary' : v >= 90 ? 'error' : v >= 75 ? 'warning' : 'success');
const jitter = (v, lo, hi) => Math.min(hi, Math.max(lo, v + Math.round(Math.random() * 12 - 6)));
// Sparkline data/labels/formatter are object props — assign via an action.
function spark(node, host) {
const apply = (h) => {
node.data = h.latency.slice();
node.labels = TIMES.slice();
node.valueFormatter = fmtMs;
};
customElements.whenDefined('md-sparkline').then(() => apply(host));
return { update: apply };
}
function onRegion(e) {
const next = e.detail[0] || 'all';
// Arm the FLIP snapshot, then filter the rows.
table.animateNextChange().then(() => (region = next));
}
function refresh() {
refreshing = true;
setTimeout(() => { // stands in for the real readings fetch
const time = new Date().toISOString().slice(11, 19);
hosts = hosts.map((h) => {
if (h.status === 'down') return { ...h, check: 'Last check ' + time + ' UTC — connection refused after 3 retries' };
const latency = h.latency.slice(1).concat(jitter(h.latency[7], 20, 240));
return { ...h, latency, cpu: jitter(h.cpu, 5, 98), mem: jitter(h.mem, 5, 98),
check: 'Last check ' + time + ' UTC — agent v2.14, HTTP 200 in ' + latency[7] + ' ms' };
});
updated = time;
refreshing = false;
}, 900);
}
</script>
<div class="fleet-toolbar">
<span>Updated {updated} UTC</span>
<md-loading-indicator hidden={!refreshing} label="Refreshing readings"></md-loading-indicator>
<md-segmented-button-set aria-label="Region filter" density="-1" on:mdChange={onRegion}>
<md-segmented-button value="all" label="All" selected={region === 'all'}></md-segmented-button>
<md-segmented-button value="us-east" label="us-east" selected={region === 'us-east'}></md-segmented-button>
<md-segmented-button value="eu-west" label="eu-west" selected={region === 'eu-west'}></md-segmented-button>
</md-segmented-button-set>
<md-button variant="tonal" icon="refresh" disabled={refreshing} on:mdClick={refresh}>Refresh</md-button>
</div>
<md-table-container variant="outlined">
<md-table bind:this={table} label="Server fleet status" min-width="640px"
column-template="minmax(130px, 1.2fr) 150px minmax(100px, 1fr) minmax(100px, 1fr) 130px">
<md-table-head>
<md-table-row rowgroup="head">
<md-table-cell head scope="col">Host</md-table-cell>
<md-table-cell head scope="col">Status</md-table-cell>
<md-table-cell head scope="col">CPU</md-table-cell>
<md-table-cell head scope="col">Memory</md-table-cell>
<md-table-cell head scope="col">Latency p95</md-table-cell>
</md-table-row>
</md-table-head>
<md-table-body>
{#each visible as h (h.id)}
<md-table-row value={h.id}>
<md-table-cell ellipsis>{h.name}</md-table-cell>
<md-table-cell>
<md-tooltip text={h.check} position="bottom">
<span tabindex="0" class="host-status">
<md-status-dot state={DOT[h.status]} live={h.status === 'down'}></md-status-dot>
{WORD[h.status]}
</span>
</md-tooltip>
</md-table-cell>
<md-table-cell>
<md-meter label={'CPU load, ' + h.id} value={h.cpu ?? 0} show-value
value-text={h.cpu == null ? 'no data' : undefined} color={meterColor(h.cpu)}></md-meter>
</md-table-cell>
<md-table-cell>
<md-meter label={'Memory used, ' + h.id} value={h.mem ?? 0} show-value
value-text={h.mem == null ? 'no data' : undefined} color={meterColor(h.mem)}></md-meter>
</md-table-cell>
<md-table-cell>
<md-sparkline use:spark={h} min="0" max="250" height="24px" color={SPARK[h.status]}></md-sparkline>
</md-table-cell>
</md-table-row>
{/each}
</md-table-body>
</md-table>
</md-table-container>How it’s built
Section titled “How it’s built”| Component | Role in this screen |
|---|---|
md-table | The board. A hand-authored CSS-Grid table — column-template declares the five tracks, min-width="640px" trades squished columns for a horizontal scroll inside md-table-container, and animateNextChange() FLIP-animates the region filter. |
md-status-dot | Health pip beside the state word: online = healthy, away = degraded, busy = down. The down host adds live — the one genuinely-changing state on the board gets the pulse. |
md-meter | CPU and memory readings. show-value renders the percentage, color swaps success → warning → error as thresholds are crossed, and the unreachable host shows value-text="no data" so the same string drives the visible text and aria-valuetext. |
md-sparkline | Latency trend per row. Every instance pins min="0" max="250", so six sparklines share one comparable scale instead of each auto-scaling to its own drama. |
md-tooltip | Last-check details on hover and keyboard focus — the trigger span carries tabindex="0" because a bare span gets hover only. |
md-segmented-button-set | Single-select region filter (role="radiogroup"); its mdChange payload is the array of selected values. |
md-loading-indicator | Indeterminate refresh moment next to the md-button that triggers it — removed when readings land, never left spinning. |
The details that make it production-grade
Section titled “The details that make it production-grade”- Status is words, not colour alone. Each dot sits beside a visible
“Healthy / Degraded / Down” word, so the state survives forced-colors mode
(where every dot collapses to one system colour) and satisfies WCAG 1.4.1 —
the dots themselves stay decorative (
role="presentation") because the text carries the meaning. - The sparklines share a fixed scale. Left to auto-scale, a 36–41 ms flat
line and a 60–250 ms meltdown would look identical; pinning
min/maxacross all six rows is what makes them comparable at a glance. - Filtering can’t make the page jump.
md-table’s height ratchet (keep-height, on by default) keeps the board at its initial height when a region filter drops rows, andanimateNextChange()glides survivors into place — withprefers-reduced-motionwinning over both the FLIP and the dot’s pulse automatically. - The tooltip is reachable without a mouse. The trigger span’s
tabindex="0"gives keyboard users the same last-check detail, the popup stays hoverable across the gap (WCAG 1.4.13), andEscapedismisses it. The tooltip setsaria-descriptionon the trigger — a description, never the accessible name. - One string per reading, seen and spoken.
md-meterclampsvalueinto range, andvalue-textoverrides the visible value andaria-valuetexttogether, so the “no data” state on the down host can never drift between what sighted users see and what screen readers announce.
Variations
Section titled “Variations”- Large fleets: add
frozen-headerplusmd-table-container max-height="60vh"so the header stays put while hundreds of hosts scroll; with pagination, setrow-offset/row-countso AT announces “row 41 of 480” instead of a per-page position. - Sortable hot-spots: wrap the CPU header in an
md-table-sort-label column="cpu" default-order="desc"and reorder rows in themdSortChangehandler — the table animates the reorder for free. - Bulk actions:
selection="multiple"with a checkbox column turns the board into a target list for “silence alerts” or “drain and reboot”. - Tile view: the same meters as
variant="circular"rings (show-value show-label) make a per-host card wall from identical data.
Related
Section titled “Related”- Recipe: Two-factor verification — the same loading-indicator pattern gating an async check.
- Component manuals:
md-table— the grid architecture, frozen headers, and column pinning;md-sparkline— the shared-scale rule and reference areas for flagging outage windows.